├── .gitignore
├── .release-it.json
├── .vscode
└── settings.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUING.md
├── LICENSE
├── README.md
├── __.eslint.config.js
├── package-lock.json
├── package.json
├── packages
├── codetie
│ ├── .npmrc
│ ├── .prettierrc
│ ├── CHANGELOG.md
│ ├── bin
│ │ └── codetie.mjs
│ ├── build.config.ts
│ ├── package.json
│ ├── playground
│ │ ├── .gitignore
│ │ ├── .swiftlint.yml
│ │ ├── codetie.yml
│ │ ├── exampleProject
│ │ │ ├── ContentView.swift
│ │ │ ├── Info.plist
│ │ │ ├── debug.xcconfig
│ │ │ └── exampleProjectApp.swift
│ │ └── project.yml
│ ├── src
│ │ ├── commands
│ │ │ ├── build.ts
│ │ │ ├── generate.ts
│ │ │ ├── index.ts
│ │ │ └── preview.ts
│ │ ├── index.ts
│ │ ├── main.ts
│ │ ├── run.ts
│ │ └── utils
│ │ │ └── fs.ts
│ ├── tsconfig.json
│ └── types.d.ts
└── create-codetie
│ ├── .gitignore
│ ├── __buildServer.json
│ ├── build.config.ts
│ ├── index.js
│ ├── package.json
│ ├── src
│ ├── index.ts
│ ├── prompts
│ │ ├── index.ts
│ │ ├── init.ts
│ │ └── installDependencies.ts
│ └── utils
│ │ └── fs.ts
│ ├── template
│ ├── .cursorrules
│ ├── .swiftlint.yml
│ ├── .vscode
│ │ ├── extensions.json
│ │ ├── settings.json
│ │ └── tasks.json
│ ├── README.md
│ ├── _gitignore
│ ├── _project
│ │ ├── ContentView.swift
│ │ ├── _main.swift
│ │ └── debug.xcconfig
│ ├── project.yml.mustache
│ ├── scripts
│ │ ├── build_project.sh
│ │ ├── build_project_server.sh
│ │ ├── parse_build_log.py
│ │ └── run_simulator.sh.mustache
│ └── simulators.yml
│ └── tsconfig.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── scripts
├── __release.ts
├── extendCommitHash.ts
├── release.ts
└── releaseUtils.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 | ## User settings
5 | xcuserdata/
6 |
7 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
8 | *.xcscmblueprint
9 | *.xccheckout
10 |
11 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
12 | build/
13 | DerivedData/
14 | *.moved-aside
15 | *.pbxuser
16 | !default.pbxuser
17 | *.mode1v3
18 | !default.mode1v3
19 | *.mode2v3
20 | !default.mode2v3
21 | *.perspectivev3
22 | !default.perspectivev3
23 |
24 | ## Obj-C/Swift specific
25 | *.hmap
26 |
27 | ## App packaging
28 | *.ipa
29 | *.dSYM.zip
30 | *.dSYM
31 |
32 | ## Playgrounds
33 | timeline.xctimeline
34 | playground.xcworkspace
35 |
36 | # Swift Package Manager
37 | #
38 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
39 | # Packages/
40 | # Package.pins
41 | # Package.resolved
42 | # *.xcodeproj
43 | #
44 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
45 | # hence it is not needed unless you have added a package configuration file to your project
46 | # .swiftpm
47 |
48 | .build/
49 |
50 | # CocoaPods
51 | #
52 | # We recommend against adding the Pods directory to your .gitignore. However
53 | # you should judge for yourself, the pros and cons are mentioned at:
54 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
55 | #
56 | # Pods/
57 | #
58 | # Add this line if you want to avoid checking in source code from the Xcode workspace
59 | # *.xcworkspace
60 |
61 | # Carthage
62 | #
63 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
64 | # Carthage/Checkouts
65 |
66 | Carthage/Build/
67 |
68 | # Accio dependency management
69 | Dependencies/
70 | .accio/
71 |
72 | # fastlane
73 | #
74 | # It is recommended to not store the screenshots in the git repo.
75 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
76 | # For more information about the recommended setup visit:
77 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
78 |
79 | fastlane/report.xml
80 | fastlane/Preview.html
81 | fastlane/screenshots/**/*.png
82 | fastlane/test_output
83 |
84 | # Code Injection
85 | #
86 | # After new code Injection tools there's a generated folder /iOSInjectionProject
87 | # https://github.com/johnno1962/injectionforxcode
88 |
89 | iOSInjectionProject/
90 |
91 | # macOS
92 | .DS_Store
93 |
94 | # SwiftUI Preview Content
95 | **/Preview Content
96 |
97 |
98 | node_modules/
99 | dist
100 | .DS_Store
101 | .history
102 | .vercel
103 | .idea
104 | .env
105 | .data
106 | packages/create-codetie/debug.log
107 |
--------------------------------------------------------------------------------
/.release-it.json:
--------------------------------------------------------------------------------
1 | {
2 | "git": {
3 | "commitMessage": "chore: release v${version}",
4 | "tagName": "v${version}",
5 | "pushRepo": "origin main"
6 | },
7 | "npm": {
8 | "publish": false
9 | },
10 | "plugins": {
11 | "@release-it/conventional-changelog": {
12 | "preset": "angular",
13 | "infile": "CHANGELOG.md"
14 | },
15 | "@release-it/bumper": {
16 | "out": [
17 | "packages/*/package.json"
18 | ]
19 | }
20 | },
21 | "hooks": {
22 | "after:bump": "pnpm install",
23 | "after:git:release": "echo Successfully released ${name} v${version} to ${repo.repository}.",
24 | "after:release": "echo Successfully released ${name} v${version}. Now you can publish the packages individually."
25 | }
26 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "lldb.library": "/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Versions/A/LLDB",
3 | "lldb.launch.expressions": "native"
4 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## [1.0.9](https://github.com/codetie-ai/codetie/compare/v1.0.8...v1.0.9) (2024-09-25)
4 |
5 | ## [1.0.8](https://github.com/codetie-ai/codetie/compare/v1.0.7...v1.0.8) (2024-09-25)
6 |
7 | ## [1.0.7](https://github.com/codetie-ai/codetie/compare/v1.0.6...v1.0.7) (2024-09-18)
8 |
9 | ## [1.0.6](https://github.com/codetie-ai/codetie/compare/v1.0.5...v1.0.6) (2024-09-16)
10 |
11 | ## [1.0.5](https://github.com/codetie-ai/codetie/compare/v1.0.4...v1.0.5) (2024-09-16)
12 |
13 | ## [1.0.4](https://github.com/codetie-ai/codetie/compare/v1.0.3...v1.0.4) (2024-09-16)
14 |
15 | ## [1.0.3](https://github.com/codetie-ai/codetie/compare/v1.0.2...v1.0.3) (2024-09-16)
16 |
17 | ## [1.0.2](https://github.com/codetie-ai/codetie/compare/v1.0.1...v1.0.2) (2024-09-16)
18 |
19 | # Changelog
20 |
21 | ## 1.0.1 (2024-09-15)
22 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 | - Using welcoming and inclusive language
11 | - Being respectful of differing viewpoints and experiences
12 | - Gracefully accepting constructive criticism
13 | - Focusing on what is best for the community
14 | - Showing empathy towards other community members
15 |
16 | Examples of unacceptable behavior by participants include:
17 | - The use of sexualized language or imagery and unwelcome sexual attention or advances
18 | - Trolling, insulting/derogatory comments, and personal or political attacks
19 | - Public or private harassment
20 | - Publishing others' private information, such as a physical or electronic address, without explicit permission
21 | - Other conduct which could reasonably be considered inappropriate in a professional setting
22 |
23 | ## Our Responsibilities
24 |
25 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
26 |
27 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
28 |
29 | ## Scope
30 |
31 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
32 |
33 | ## Enforcement
34 |
35 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [INSERT EMAIL ADDRESS]. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
36 |
37 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
38 |
39 | ## Attribution
40 |
41 | This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
42 |
43 | For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq
44 |
--------------------------------------------------------------------------------
/CONTRIBUING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thank you for considering contributing to our project! We welcome all kinds of contributions, including bug reports, feature requests, documentation improvements, and code enhancements.
4 |
5 | ## How to Contribute
6 |
7 | 1. **Fork the Repository**: Start by forking the repository to your GitHub account.
8 |
9 | 2. **Clone the Repository**: Clone your forked repository to your local machine.
10 | ```bash
11 | git clone https://github.com/your-username/repository-name.git
12 | ```
13 |
14 | 3. **Create a Branch**: Create a new branch for your contribution.
15 | ```bash
16 | git checkout -b feature/your-feature-name
17 | ```
18 |
19 | 4. **Make Changes**: Make your changes in the new branch. Ensure your code follows the project's coding standards and includes appropriate tests.
20 |
21 | 5. **Commit Changes**: Commit your changes with a clear and concise commit message.
22 | ```bash
23 | git commit -m "Add feature: your feature description"
24 | ```
25 |
26 | 6. **Push Changes**: Push your changes to your forked repository.
27 | ```bash
28 | git push origin feature/your-feature-name
29 | ```
30 |
31 | 7. **Create a Pull Request**: Open a pull request from your branch to the main repository's `main` branch. Provide a detailed description of your changes and any related issues.
32 |
33 | ## Code of Conduct
34 |
35 | Please note that this project is governed by a [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code.
36 |
37 | ## License
38 |
39 | By contributing, you agree that your contributions will be licensed under the [MIT License](LICENSE).
40 |
41 | ## Contact
42 |
43 | If you have any questions or need further assistance, feel free to contact the project maintainers.
44 |
45 | Thank you for your contributions!
46 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Nuxt Project
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Codetie.ai - Cursor-XCode-Swift-Sync
2 |
3 | Cursor-XCode-Swift-Sync is a project boilerplate that leverages the power of [XcodeGen](https://github.com/yonaskolb/XcodeGen) to streamline the Xcode project generation process for Swift applications.
4 |
5 | Read more at [codetie.ai](https://codetie.ai)
6 |
7 | [](https://res.cloudinary.com/ddbi0suli/video/upload/v1725936511/codetie/twitter_cursor_swift2_jnarb7.mp4)
8 |
9 |
10 | ## Features
11 |
12 | - Automated Xcode project generation
13 | - Simplified project configuration using YAML or JSON
14 | - Easy management of targets, configurations, and schemes
15 | - Seamless integration with version control systems
16 | - VS Code tasks for project generation, building, and linting
17 |
18 | ## Prerequisites
19 |
20 | Before you begin, ensure you have the following installed:
21 | - Xcode (latest stable version)
22 | - [XcodeGen](https://github.com/yonaskolb/XcodeGen)
23 | - [SwiftLint](https://github.com/realm/SwiftLint) (optional, for linting)
24 |
25 | ## Installation
26 |
27 | Read docs at [codetie.ai](https://codetie.ai/docs)
28 |
29 | ## Tasks
30 |
31 | 4. Use VS Code tasks to manage your project:
32 | - Generate Xcode Project: `CMD+SHIFT+B` > "Tasks: Run Task" > "Generate Xcode Project with XcodeGen"
33 | - Build Swift Project: `CMD+SHIFT+B` > "Tasks: Run Task" > "Build Swift Project"
34 | - Run SwiftLint: `CMD+SHIFT+B` > "Tasks: Run Task" > "Run SwiftLint"
35 |
36 | ## Project Structure
37 |
38 | ```
39 | Cursor-XCode-Swift-Sync/
40 | ├── Sources/
41 | │ └── (Your Swift source files)
42 | ├── Tests/
43 | │ └── (Your test files)
44 | ├── Resources/
45 | │ └── (Asset catalogs, storyboards, etc.)
46 | ├── project.yml
47 | └── README.md
48 | ```
49 |
50 | ## Built with Cursor-XCode-Swift-Sync
51 |
52 | [Flappy Bird Clone in Swift](https://github.com/regenrek/flappy-bird-clone-swift)
53 | [](https://res.cloudinary.com/ddbi0suli/video/upload/v1726393407/codetie/flappy_bird_zai8ig_5_s7kmjt.mp4)
54 |
55 |
56 | ## Customization
57 |
58 | Edit the `project.yml` file to customize your project settings, targets, and configurations. Refer to the [XcodeGen documentation](https://github.com/yonaskolb/XcodeGen/blob/master/Docs/ProjectSpec.md) for detailed information on available options.
59 |
60 | ## Benefits of Using XcodeGen
61 |
62 | - **Version Control Friendly**: Keep your Xcode project configuration in a simple, readable format.
63 | - **Reduced Merge Conflicts**: Eliminate `.xcodeproj` merge conflicts in team environments.
64 | - **Consistency**: Ensure project structure consistency across team members and CI systems.
65 | - **Automation**: Easily integrate project generation into your development and CI workflows.
66 |
67 | ## Contributing
68 |
69 | Contributions are welcome! Please feel free to submit a Pull Request.
70 |
71 | 1. Clone this repository:
72 |
73 | ```
74 | git clone https://github.com/regenrek/Cursor-XCode-Swift-Sync
75 | ```
76 |
77 | ## License
78 |
79 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
80 |
81 | ## Acknowledgments
82 |
83 | - [XcodeGen](https://github.com/yonaskolb/XcodeGen) for making Xcode project management a breeze.
84 |
85 | ---
86 |
87 | Feel free to customize this README further to match the specific details and requirements of your Cursor2Swift project. The structure provided gives a solid foundation for explaining your project's purpose, setup process, and the benefits of using XcodeGen in your workflow.
--------------------------------------------------------------------------------
/__.eslint.config.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | import { builtinModules, createRequire } from 'node:module'
3 | import eslint from '@eslint/js'
4 | import pluginN from 'eslint-plugin-n'
5 | import pluginImportX from 'eslint-plugin-import-x'
6 | import pluginRegExp from 'eslint-plugin-regexp'
7 | import tseslint from 'typescript-eslint'
8 | import globals from 'globals'
9 |
10 | const require = createRequire(import.meta.url)
11 | const pkg = require('./package.json')
12 | const pkgVite = require('./packages/codetie/package.json')
13 |
14 | // Some rules work better with typechecking enabled, but as enabling it is slow,
15 | // we only do so when linting in IDEs for now. If you want to lint with typechecking
16 | // explicitly, set this to `true` manually.
17 | const shouldTypeCheck = typeof process.env.VSCODE_PID === 'string'
18 |
19 | export default tseslint.config(
20 | {
21 | ignores: [
22 | 'packages/create-codetie/template-*',
23 | '**/dist/**',
24 | '**/fixtures/**',
25 | '**/playground-temp/**',
26 | '**/temp/**',
27 | '**/*.snap',
28 | ],
29 | },
30 | eslint.configs.recommended,
31 | ...tseslint.configs.recommended,
32 | ...tseslint.configs.stylistic,
33 | pluginRegExp.configs['flat/recommended'],
34 | {
35 | name: 'main',
36 | languageOptions: {
37 | parser: tseslint.parser,
38 | parserOptions: {
39 | sourceType: 'module',
40 | ecmaVersion: 2022,
41 | project: shouldTypeCheck
42 | ? [
43 | './packages/*/tsconfig.json',
44 | './packages/codetie/src/*/tsconfig.json',
45 | ]
46 | : undefined,
47 | },
48 | globals: {
49 | ...globals.es2021,
50 | ...globals.node,
51 | },
52 | },
53 | plugins: {
54 | n: pluginN,
55 | 'import-x': pluginImportX,
56 | },
57 | rules: {
58 | 'n/no-exports-assign': 'error',
59 | 'n/no-unpublished-bin': 'error',
60 | 'n/no-unsupported-features/es-builtins': 'error',
61 | 'n/no-unsupported-features/node-builtins': 'error',
62 | 'n/process-exit-as-throw': 'error',
63 | 'n/hashbang': 'error',
64 |
65 | eqeqeq: ['warn', 'always', { null: 'never' }],
66 | 'no-debugger': ['error'],
67 | 'no-empty': ['warn', { allowEmptyCatch: true }],
68 | 'no-process-exit': 'off',
69 | 'prefer-const': [
70 | 'warn',
71 | {
72 | destructuring: 'all',
73 | },
74 | ],
75 |
76 | 'n/no-missing-require': [
77 | 'error',
78 | {
79 | // for try-catching yarn pnp
80 | allowModules: ['pnpapi', 'codetie'],
81 | tryExtensions: ['.ts', '.js', '.jsx', '.tsx', '.d.ts'],
82 | },
83 | ],
84 | 'n/no-extraneous-import': [
85 | 'error',
86 | {
87 | allowModules: ['codetie', 'less', 'sass', 'vitest', 'unbuild'],
88 | },
89 | ],
90 | 'n/no-extraneous-require': [
91 | 'error',
92 | {
93 | allowModules: ['codetie'],
94 | },
95 | ],
96 |
97 | '@typescript-eslint/ban-ts-comment': 'error',
98 | '@typescript-eslint/no-unsafe-function-type': 'off',
99 | '@typescript-eslint/explicit-module-boundary-types': [
100 | 'error',
101 | { allowArgumentsExplicitlyTypedAsAny: true },
102 | ],
103 | '@typescript-eslint/no-empty-function': [
104 | 'error',
105 | { allow: ['arrowFunctions'] },
106 | ],
107 | '@typescript-eslint/no-empty-object-type': [
108 | 'error',
109 | { allowInterfaces: 'with-single-extends' },
110 | ],
111 | '@typescript-eslint/no-empty-interface': 'off',
112 | '@typescript-eslint/no-explicit-any': 'off',
113 | 'no-extra-semi': 'off',
114 | '@typescript-eslint/no-extra-semi': 'off', // conflicts with prettier
115 | '@typescript-eslint/no-inferrable-types': 'off',
116 | '@typescript-eslint/no-unused-vars': [
117 | 'error',
118 | {
119 | args: 'all',
120 | argsIgnorePattern: '^_',
121 | caughtErrors: 'all',
122 | caughtErrorsIgnorePattern: '^_',
123 | destructuredArrayIgnorePattern: '^_',
124 | varsIgnorePattern: '^_',
125 | ignoreRestSiblings: true,
126 | },
127 | ],
128 | '@typescript-eslint/no-require-imports': 'off',
129 | '@typescript-eslint/consistent-type-imports': [
130 | 'error',
131 | { prefer: 'type-imports', disallowTypeAnnotations: false },
132 | ],
133 | // disable rules set in @typescript-eslint/stylistic which conflict with current code
134 | // we should discuss if we want to enable these as they encourage consistent code
135 | '@typescript-eslint/array-type': 'off',
136 | '@typescript-eslint/consistent-type-definitions': 'off',
137 | '@typescript-eslint/prefer-for-of': 'off',
138 | '@typescript-eslint/prefer-function-type': 'off',
139 |
140 | 'import-x/no-nodejs-modules': [
141 | 'error',
142 | { allow: builtinModules.map((mod) => `node:${mod}`) },
143 | ],
144 | 'import-x/no-duplicates': 'error',
145 | 'import-x/order': 'error',
146 | 'sort-imports': [
147 | 'error',
148 | {
149 | ignoreCase: false,
150 | ignoreDeclarationSort: true,
151 | ignoreMemberSort: false,
152 | memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'],
153 | allowSeparatedGroups: false,
154 | },
155 | ],
156 |
157 | 'regexp/prefer-regexp-exec': 'error',
158 | 'regexp/prefer-regexp-test': 'error',
159 | // in some cases using explicit letter-casing is more performant than the `i` flag
160 | 'regexp/use-ignore-case': 'off',
161 | },
162 | },
163 | {
164 | name: 'vite/globals',
165 | files: ['packages/**/*.?([cm])[jt]s?(x)'],
166 | ignores: ['**/__tests__/**'],
167 | rules: {
168 | 'no-restricted-globals': ['error', 'require', '__dirname', '__filename'],
169 | },
170 | },
171 | {
172 | name: 'vite/node',
173 | files: ['packages/vite/src/node/**/*.?([cm])[jt]s?(x)'],
174 | rules: {
175 | 'no-console': ['error'],
176 | 'n/no-restricted-require': [
177 | 'error',
178 | Object.keys(pkgVite.devDependencies).map((d) => ({
179 | name: d,
180 | message:
181 | `devDependencies can only be imported using ESM syntax so ` +
182 | `that they are included in the rollup bundle. If you are trying to ` +
183 | `lazy load a dependency, use (await import('dependency')).default instead.`,
184 | })),
185 | ],
186 | },
187 | },
188 | {
189 | name: 'playground/enforce-esm',
190 | files: ['playground/**/*.?([cm])[jt]s?(x)'],
191 | ignores: [
192 | 'playground/ssr-resolve/**',
193 | 'playground/**/*{commonjs,cjs}*/**',
194 | 'playground/**/*{commonjs,cjs}*',
195 | 'playground/**/*dep*/**',
196 | 'playground/resolve/browser-module-field2/index.web.js',
197 | 'playground/resolve/browser-field/**',
198 | 'playground/tailwind/**', // blocked by https://github.com/postcss/postcss-load-config/issues/239
199 | ],
200 | rules: {
201 | 'import-x/no-commonjs': 'error',
202 | },
203 | },
204 | {
205 | name: 'playground/test',
206 | files: ['playground/**/__tests__/**/*.?([cm])[jt]s?(x)'],
207 | rules: {
208 | // engine field doesn't exist in playgrounds
209 | 'n/no-unsupported-features/es-builtins': [
210 | 'error',
211 | {
212 | version: pkg.engines.node,
213 | },
214 | ],
215 | 'n/no-unsupported-features/node-builtins': [
216 | 'error',
217 | {
218 | version: pkg.engines.node,
219 | // ideally we would like to allow all experimental features
220 | // https://github.com/eslint-community/eslint-plugin-n/issues/199
221 | ignores: ['fetch'],
222 | },
223 | ],
224 | },
225 | },
226 |
227 | {
228 | name: 'disables/vite/client',
229 | files: ['packages/vite/src/client/**/*.?([cm])[jt]s?(x)'],
230 | ignores: ['**/__tests__/**'],
231 | rules: {
232 | 'n/no-unsupported-features/node-builtins': 'off',
233 | },
234 | },
235 | {
236 | name: 'disables/vite/types',
237 | files: [
238 | 'packages/vite/src/types/**/*.?([cm])[jt]s?(x)',
239 | 'packages/vite/scripts/**/*.?([cm])[jt]s?(x)',
240 | '**/*.spec.ts',
241 | ],
242 | rules: {
243 | 'n/no-extraneous-import': 'off',
244 | },
245 | },
246 | {
247 | name: 'disables/vite/cjs',
248 | files: ['packages/vite/index.cjs'],
249 | rules: {
250 | 'no-restricted-globals': 'off',
251 | 'n/no-missing-require': 'off',
252 | },
253 | },
254 | {
255 | name: 'disables/create-vite/templates',
256 | files: [
257 | 'packages/create-vite/template-*/**/*.?([cm])[jt]s?(x)',
258 | '**/build.config.ts',
259 | ],
260 | rules: {
261 | 'no-undef': 'off',
262 | 'n/no-missing-import': 'off',
263 | 'n/no-extraneous-import': 'off',
264 | 'n/no-extraneous-require': 'off',
265 | '@typescript-eslint/explicit-module-boundary-types': 'off',
266 | },
267 | },
268 | {
269 | name: 'disables/playground',
270 | files: ['playground/**/*.?([cm])[jt]s?(x)', 'docs/**/*.?([cm])[jt]s?(x)'],
271 | rules: {
272 | 'n/no-extraneous-import': 'off',
273 | 'n/no-extraneous-require': 'off',
274 | 'n/no-missing-import': 'off',
275 | 'n/no-missing-require': 'off',
276 | 'n/no-unsupported-features/es-builtins': 'off',
277 | 'n/no-unsupported-features/node-builtins': 'off',
278 | '@typescript-eslint/explicit-module-boundary-types': 'off',
279 | '@typescript-eslint/no-unused-expressions': 'off',
280 | '@typescript-eslint/no-unused-vars': 'off',
281 | 'no-undef': 'off',
282 | 'no-empty': 'off',
283 | 'no-constant-condition': 'off',
284 | '@typescript-eslint/no-empty-function': 'off',
285 | },
286 | },
287 | {
288 | name: 'disables/playground/tsconfig-json',
289 | files: [
290 | 'playground/tsconfig-json/**/*.?([cm])[jt]s?(x)',
291 | 'playground/tsconfig-json-load-error/**/*.?([cm])[jt]s?(x)',
292 | ],
293 | ignores: ['**/__tests__/**'],
294 | rules: {
295 | '@typescript-eslint/ban-ts-comment': 'off',
296 | },
297 | },
298 | {
299 | name: 'disables/js',
300 | files: ['**/*.js', '**/*.mjs', '**/*.cjs'],
301 | rules: {
302 | '@typescript-eslint/explicit-module-boundary-types': 'off',
303 | },
304 | },
305 | {
306 | name: 'disables/dts',
307 | files: ['**/*.d.ts'],
308 | rules: {
309 | '@typescript-eslint/consistent-indexed-object-style': 'off',
310 | '@typescript-eslint/triple-slash-reference': 'off',
311 | },
312 | },
313 | {
314 | name: 'disables/test',
315 | files: ['**/__tests__/**/*.?([cm])[jt]s?(x)'],
316 | rules: {
317 | 'no-console': 'off',
318 | '@typescript-eslint/ban-ts-comment': 'off',
319 | },
320 | },
321 | {
322 | name: 'disables/typechecking',
323 | files: [
324 | '**/*.js',
325 | '**/*.mjs',
326 | '**/*.cjs',
327 | '**/*.d.ts',
328 | '**/*.d.cts',
329 | '**/__tests__/**',
330 | 'docs/**',
331 | 'playground/**',
332 | 'scripts/**',
333 | 'vitest.config.ts',
334 | 'vitest.config.e2e.ts',
335 | ],
336 | languageOptions: {
337 | parserOptions: {
338 | project: false,
339 | },
340 | },
341 | },
342 | )
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Cursor-XCode-Swift-Sync",
3 | "lockfileVersion": 3,
4 | "requires": true,
5 | "packages": {
6 | "": {
7 | "dependencies": {
8 | "chalk": "^5.3.0",
9 | "inquirer": "^10.2.2",
10 | "mustache": "^4.2.0",
11 | "yargs": "^17.7.2"
12 | },
13 | "version": "1.0.9"
14 | },
15 | "node_modules/@inquirer/checkbox": {
16 | "version": "2.5.0",
17 | "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-2.5.0.tgz",
18 | "integrity": "sha512-sMgdETOfi2dUHT8r7TT1BTKOwNvdDGFDXYWtQ2J69SvlYNntk9I/gJe7r5yvMwwsuKnYbuRs3pNhx4tgNck5aA==",
19 | "dependencies": {
20 | "@inquirer/core": "^9.1.0",
21 | "@inquirer/figures": "^1.0.5",
22 | "@inquirer/type": "^1.5.3",
23 | "ansi-escapes": "^4.3.2",
24 | "yoctocolors-cjs": "^2.1.2"
25 | },
26 | "engines": {
27 | "node": ">=18"
28 | }
29 | },
30 | "node_modules/@inquirer/confirm": {
31 | "version": "3.2.0",
32 | "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.2.0.tgz",
33 | "integrity": "sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==",
34 | "dependencies": {
35 | "@inquirer/core": "^9.1.0",
36 | "@inquirer/type": "^1.5.3"
37 | },
38 | "engines": {
39 | "node": ">=18"
40 | }
41 | },
42 | "node_modules/@inquirer/core": {
43 | "version": "9.1.0",
44 | "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.1.0.tgz",
45 | "integrity": "sha512-RZVfH//2ytTjmaBIzeKT1zefcQZzuruwkpTwwbe/i2jTl4o9M+iML5ChULzz6iw1Ok8iUBBsRCjY2IEbD8Ft4w==",
46 | "dependencies": {
47 | "@inquirer/figures": "^1.0.5",
48 | "@inquirer/type": "^1.5.3",
49 | "@types/mute-stream": "^0.0.4",
50 | "@types/node": "^22.5.2",
51 | "@types/wrap-ansi": "^3.0.0",
52 | "ansi-escapes": "^4.3.2",
53 | "cli-spinners": "^2.9.2",
54 | "cli-width": "^4.1.0",
55 | "mute-stream": "^1.0.0",
56 | "signal-exit": "^4.1.0",
57 | "strip-ansi": "^6.0.1",
58 | "wrap-ansi": "^6.2.0",
59 | "yoctocolors-cjs": "^2.1.2"
60 | },
61 | "engines": {
62 | "node": ">=18"
63 | }
64 | },
65 | "node_modules/@inquirer/editor": {
66 | "version": "2.2.0",
67 | "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-2.2.0.tgz",
68 | "integrity": "sha512-9KHOpJ+dIL5SZli8lJ6xdaYLPPzB8xB9GZItg39MBybzhxA16vxmszmQFrRwbOA918WA2rvu8xhDEg/p6LXKbw==",
69 | "dependencies": {
70 | "@inquirer/core": "^9.1.0",
71 | "@inquirer/type": "^1.5.3",
72 | "external-editor": "^3.1.0"
73 | },
74 | "engines": {
75 | "node": ">=18"
76 | }
77 | },
78 | "node_modules/@inquirer/expand": {
79 | "version": "2.3.0",
80 | "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-2.3.0.tgz",
81 | "integrity": "sha512-qnJsUcOGCSG1e5DTOErmv2BPQqrtT6uzqn1vI/aYGiPKq+FgslGZmtdnXbhuI7IlT7OByDoEEqdnhUnVR2hhLw==",
82 | "dependencies": {
83 | "@inquirer/core": "^9.1.0",
84 | "@inquirer/type": "^1.5.3",
85 | "yoctocolors-cjs": "^2.1.2"
86 | },
87 | "engines": {
88 | "node": ">=18"
89 | }
90 | },
91 | "node_modules/@inquirer/figures": {
92 | "version": "1.0.5",
93 | "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.5.tgz",
94 | "integrity": "sha512-79hP/VWdZ2UVc9bFGJnoQ/lQMpL74mGgzSYX1xUqCVk7/v73vJCMw1VuyWN1jGkZ9B3z7THAbySqGbCNefcjfA==",
95 | "engines": {
96 | "node": ">=18"
97 | }
98 | },
99 | "node_modules/@inquirer/input": {
100 | "version": "2.3.0",
101 | "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-2.3.0.tgz",
102 | "integrity": "sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw==",
103 | "dependencies": {
104 | "@inquirer/core": "^9.1.0",
105 | "@inquirer/type": "^1.5.3"
106 | },
107 | "engines": {
108 | "node": ">=18"
109 | }
110 | },
111 | "node_modules/@inquirer/number": {
112 | "version": "1.1.0",
113 | "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-1.1.0.tgz",
114 | "integrity": "sha512-ilUnia/GZUtfSZy3YEErXLJ2Sljo/mf9fiKc08n18DdwdmDbOzRcTv65H1jjDvlsAuvdFXf4Sa/aL7iw/NanVA==",
115 | "dependencies": {
116 | "@inquirer/core": "^9.1.0",
117 | "@inquirer/type": "^1.5.3"
118 | },
119 | "engines": {
120 | "node": ">=18"
121 | }
122 | },
123 | "node_modules/@inquirer/password": {
124 | "version": "2.2.0",
125 | "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-2.2.0.tgz",
126 | "integrity": "sha512-5otqIpgsPYIshqhgtEwSspBQE40etouR8VIxzpJkv9i0dVHIpyhiivbkH9/dGiMLdyamT54YRdGJLfl8TFnLHg==",
127 | "dependencies": {
128 | "@inquirer/core": "^9.1.0",
129 | "@inquirer/type": "^1.5.3",
130 | "ansi-escapes": "^4.3.2"
131 | },
132 | "engines": {
133 | "node": ">=18"
134 | }
135 | },
136 | "node_modules/@inquirer/prompts": {
137 | "version": "5.5.0",
138 | "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-5.5.0.tgz",
139 | "integrity": "sha512-BHDeL0catgHdcHbSFFUddNzvx/imzJMft+tWDPwTm3hfu8/tApk1HrooNngB2Mb4qY+KaRWF+iZqoVUPeslEog==",
140 | "dependencies": {
141 | "@inquirer/checkbox": "^2.5.0",
142 | "@inquirer/confirm": "^3.2.0",
143 | "@inquirer/editor": "^2.2.0",
144 | "@inquirer/expand": "^2.3.0",
145 | "@inquirer/input": "^2.3.0",
146 | "@inquirer/number": "^1.1.0",
147 | "@inquirer/password": "^2.2.0",
148 | "@inquirer/rawlist": "^2.3.0",
149 | "@inquirer/search": "^1.1.0",
150 | "@inquirer/select": "^2.5.0"
151 | },
152 | "engines": {
153 | "node": ">=18"
154 | }
155 | },
156 | "node_modules/@inquirer/rawlist": {
157 | "version": "2.3.0",
158 | "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-2.3.0.tgz",
159 | "integrity": "sha512-zzfNuINhFF7OLAtGHfhwOW2TlYJyli7lOUoJUXw/uyklcwalV6WRXBXtFIicN8rTRK1XTiPWB4UY+YuW8dsnLQ==",
160 | "dependencies": {
161 | "@inquirer/core": "^9.1.0",
162 | "@inquirer/type": "^1.5.3",
163 | "yoctocolors-cjs": "^2.1.2"
164 | },
165 | "engines": {
166 | "node": ">=18"
167 | }
168 | },
169 | "node_modules/@inquirer/search": {
170 | "version": "1.1.0",
171 | "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-1.1.0.tgz",
172 | "integrity": "sha512-h+/5LSj51dx7hp5xOn4QFnUaKeARwUCLs6mIhtkJ0JYPBLmEYjdHSYh7I6GrLg9LwpJ3xeX0FZgAG1q0QdCpVQ==",
173 | "dependencies": {
174 | "@inquirer/core": "^9.1.0",
175 | "@inquirer/figures": "^1.0.5",
176 | "@inquirer/type": "^1.5.3",
177 | "yoctocolors-cjs": "^2.1.2"
178 | },
179 | "engines": {
180 | "node": ">=18"
181 | }
182 | },
183 | "node_modules/@inquirer/select": {
184 | "version": "2.5.0",
185 | "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-2.5.0.tgz",
186 | "integrity": "sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==",
187 | "dependencies": {
188 | "@inquirer/core": "^9.1.0",
189 | "@inquirer/figures": "^1.0.5",
190 | "@inquirer/type": "^1.5.3",
191 | "ansi-escapes": "^4.3.2",
192 | "yoctocolors-cjs": "^2.1.2"
193 | },
194 | "engines": {
195 | "node": ">=18"
196 | }
197 | },
198 | "node_modules/@inquirer/type": {
199 | "version": "1.5.4",
200 | "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.4.tgz",
201 | "integrity": "sha512-E9sqh3kOGvFj+/tF0RVnclE39tUoa03kDu++WyYXjjf/X0FSI5tAfSHedRWYelix9l3xBUr2klQ92eFyl0aNzQ==",
202 | "dependencies": {
203 | "mute-stream": "^1.0.0"
204 | },
205 | "engines": {
206 | "node": ">=18"
207 | }
208 | },
209 | "node_modules/@types/mute-stream": {
210 | "version": "0.0.4",
211 | "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz",
212 | "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==",
213 | "dependencies": {
214 | "@types/node": "*"
215 | }
216 | },
217 | "node_modules/@types/node": {
218 | "version": "22.5.5",
219 | "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz",
220 | "integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==",
221 | "dependencies": {
222 | "undici-types": "~6.19.2"
223 | }
224 | },
225 | "node_modules/@types/wrap-ansi": {
226 | "version": "3.0.0",
227 | "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz",
228 | "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g=="
229 | },
230 | "node_modules/ansi-escapes": {
231 | "version": "4.3.2",
232 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
233 | "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
234 | "dependencies": {
235 | "type-fest": "^0.21.3"
236 | },
237 | "engines": {
238 | "node": ">=8"
239 | },
240 | "funding": {
241 | "url": "https://github.com/sponsors/sindresorhus"
242 | }
243 | },
244 | "node_modules/ansi-regex": {
245 | "version": "5.0.1",
246 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
247 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
248 | "engines": {
249 | "node": ">=8"
250 | }
251 | },
252 | "node_modules/ansi-styles": {
253 | "version": "4.3.0",
254 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
255 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
256 | "dependencies": {
257 | "color-convert": "^2.0.1"
258 | },
259 | "engines": {
260 | "node": ">=8"
261 | },
262 | "funding": {
263 | "url": "https://github.com/chalk/ansi-styles?sponsor=1"
264 | }
265 | },
266 | "node_modules/chalk": {
267 | "version": "5.3.0",
268 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
269 | "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
270 | "engines": {
271 | "node": "^12.17.0 || ^14.13 || >=16.0.0"
272 | },
273 | "funding": {
274 | "url": "https://github.com/chalk/chalk?sponsor=1"
275 | }
276 | },
277 | "node_modules/chardet": {
278 | "version": "0.7.0",
279 | "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
280 | "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
281 | },
282 | "node_modules/cli-spinners": {
283 | "version": "2.9.2",
284 | "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
285 | "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
286 | "engines": {
287 | "node": ">=6"
288 | },
289 | "funding": {
290 | "url": "https://github.com/sponsors/sindresorhus"
291 | }
292 | },
293 | "node_modules/cli-width": {
294 | "version": "4.1.0",
295 | "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz",
296 | "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
297 | "engines": {
298 | "node": ">= 12"
299 | }
300 | },
301 | "node_modules/cliui": {
302 | "version": "8.0.1",
303 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
304 | "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
305 | "dependencies": {
306 | "string-width": "^4.2.0",
307 | "strip-ansi": "^6.0.1",
308 | "wrap-ansi": "^7.0.0"
309 | },
310 | "engines": {
311 | "node": ">=12"
312 | }
313 | },
314 | "node_modules/cliui/node_modules/wrap-ansi": {
315 | "version": "7.0.0",
316 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
317 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
318 | "dependencies": {
319 | "ansi-styles": "^4.0.0",
320 | "string-width": "^4.1.0",
321 | "strip-ansi": "^6.0.0"
322 | },
323 | "engines": {
324 | "node": ">=10"
325 | },
326 | "funding": {
327 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
328 | }
329 | },
330 | "node_modules/color-convert": {
331 | "version": "2.0.1",
332 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
333 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
334 | "dependencies": {
335 | "color-name": "~1.1.4"
336 | },
337 | "engines": {
338 | "node": ">=7.0.0"
339 | }
340 | },
341 | "node_modules/color-name": {
342 | "version": "1.1.4",
343 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
344 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
345 | },
346 | "node_modules/emoji-regex": {
347 | "version": "8.0.0",
348 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
349 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
350 | },
351 | "node_modules/escalade": {
352 | "version": "3.2.0",
353 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
354 | "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
355 | "engines": {
356 | "node": ">=6"
357 | }
358 | },
359 | "node_modules/external-editor": {
360 | "version": "3.1.0",
361 | "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
362 | "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
363 | "dependencies": {
364 | "chardet": "^0.7.0",
365 | "iconv-lite": "^0.4.24",
366 | "tmp": "^0.0.33"
367 | },
368 | "engines": {
369 | "node": ">=4"
370 | }
371 | },
372 | "node_modules/get-caller-file": {
373 | "version": "2.0.5",
374 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
375 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
376 | "engines": {
377 | "node": "6.* || 8.* || >= 10.*"
378 | }
379 | },
380 | "node_modules/iconv-lite": {
381 | "version": "0.4.24",
382 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
383 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
384 | "dependencies": {
385 | "safer-buffer": ">= 2.1.2 < 3"
386 | },
387 | "engines": {
388 | "node": ">=0.10.0"
389 | }
390 | },
391 | "node_modules/inquirer": {
392 | "version": "10.2.2",
393 | "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-10.2.2.tgz",
394 | "integrity": "sha512-tyao/4Vo36XnUItZ7DnUXX4f1jVao2mSrleV/5IPtW/XAEA26hRVsbc68nuTEKWcr5vMP/1mVoT2O7u8H4v1Vg==",
395 | "dependencies": {
396 | "@inquirer/core": "^9.1.0",
397 | "@inquirer/prompts": "^5.5.0",
398 | "@inquirer/type": "^1.5.3",
399 | "@types/mute-stream": "^0.0.4",
400 | "ansi-escapes": "^4.3.2",
401 | "mute-stream": "^1.0.0",
402 | "run-async": "^3.0.0",
403 | "rxjs": "^7.8.1"
404 | },
405 | "engines": {
406 | "node": ">=18"
407 | }
408 | },
409 | "node_modules/is-fullwidth-code-point": {
410 | "version": "3.0.0",
411 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
412 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
413 | "engines": {
414 | "node": ">=8"
415 | }
416 | },
417 | "node_modules/mustache": {
418 | "version": "4.2.0",
419 | "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
420 | "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
421 | "bin": {
422 | "mustache": "bin/mustache"
423 | }
424 | },
425 | "node_modules/mute-stream": {
426 | "version": "1.0.0",
427 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz",
428 | "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==",
429 | "engines": {
430 | "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
431 | }
432 | },
433 | "node_modules/os-tmpdir": {
434 | "version": "1.0.2",
435 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
436 | "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
437 | "engines": {
438 | "node": ">=0.10.0"
439 | }
440 | },
441 | "node_modules/require-directory": {
442 | "version": "2.1.1",
443 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
444 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
445 | "engines": {
446 | "node": ">=0.10.0"
447 | }
448 | },
449 | "node_modules/run-async": {
450 | "version": "3.0.0",
451 | "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz",
452 | "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==",
453 | "engines": {
454 | "node": ">=0.12.0"
455 | }
456 | },
457 | "node_modules/rxjs": {
458 | "version": "7.8.1",
459 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
460 | "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
461 | "dependencies": {
462 | "tslib": "^2.1.0"
463 | }
464 | },
465 | "node_modules/safer-buffer": {
466 | "version": "2.1.2",
467 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
468 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
469 | },
470 | "node_modules/signal-exit": {
471 | "version": "4.1.0",
472 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
473 | "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
474 | "engines": {
475 | "node": ">=14"
476 | },
477 | "funding": {
478 | "url": "https://github.com/sponsors/isaacs"
479 | }
480 | },
481 | "node_modules/string-width": {
482 | "version": "4.2.3",
483 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
484 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
485 | "dependencies": {
486 | "emoji-regex": "^8.0.0",
487 | "is-fullwidth-code-point": "^3.0.0",
488 | "strip-ansi": "^6.0.1"
489 | },
490 | "engines": {
491 | "node": ">=8"
492 | }
493 | },
494 | "node_modules/strip-ansi": {
495 | "version": "6.0.1",
496 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
497 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
498 | "dependencies": {
499 | "ansi-regex": "^5.0.1"
500 | },
501 | "engines": {
502 | "node": ">=8"
503 | }
504 | },
505 | "node_modules/tmp": {
506 | "version": "0.0.33",
507 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
508 | "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
509 | "dependencies": {
510 | "os-tmpdir": "~1.0.2"
511 | },
512 | "engines": {
513 | "node": ">=0.6.0"
514 | }
515 | },
516 | "node_modules/tslib": {
517 | "version": "2.7.0",
518 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
519 | "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA=="
520 | },
521 | "node_modules/type-fest": {
522 | "version": "0.21.3",
523 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
524 | "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
525 | "engines": {
526 | "node": ">=10"
527 | },
528 | "funding": {
529 | "url": "https://github.com/sponsors/sindresorhus"
530 | }
531 | },
532 | "node_modules/undici-types": {
533 | "version": "6.19.8",
534 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
535 | "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
536 | },
537 | "node_modules/wrap-ansi": {
538 | "version": "6.2.0",
539 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
540 | "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
541 | "dependencies": {
542 | "ansi-styles": "^4.0.0",
543 | "string-width": "^4.1.0",
544 | "strip-ansi": "^6.0.0"
545 | },
546 | "engines": {
547 | "node": ">=8"
548 | }
549 | },
550 | "node_modules/y18n": {
551 | "version": "5.0.8",
552 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
553 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
554 | "engines": {
555 | "node": ">=10"
556 | }
557 | },
558 | "node_modules/yargs": {
559 | "version": "17.7.2",
560 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
561 | "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
562 | "dependencies": {
563 | "cliui": "^8.0.1",
564 | "escalade": "^3.1.1",
565 | "get-caller-file": "^2.0.5",
566 | "require-directory": "^2.1.1",
567 | "string-width": "^4.2.3",
568 | "y18n": "^5.0.5",
569 | "yargs-parser": "^21.1.1"
570 | },
571 | "engines": {
572 | "node": ">=12"
573 | }
574 | },
575 | "node_modules/yargs-parser": {
576 | "version": "21.1.1",
577 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
578 | "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
579 | "engines": {
580 | "node": ">=12"
581 | }
582 | },
583 | "node_modules/yoctocolors-cjs": {
584 | "version": "2.1.2",
585 | "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz",
586 | "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==",
587 | "engines": {
588 | "node": ">=18"
589 | },
590 | "funding": {
591 | "url": "https://github.com/sponsors/sindresorhus"
592 | }
593 | }
594 | },
595 | "version": "1.0.9"
596 | }
597 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@codetie/codetie-monorepo",
3 | "private": true,
4 | "homepage": "https://codetie.ai",
5 | "engines": {
6 | "node": "^18.0.0 || >=20.0.0"
7 | },
8 | "repository": {
9 | "type": "git",
10 | "url": "git+https://github.com/codetie-ai/codetie.git"
11 | },
12 | "license": "MIT",
13 | "bin": {
14 | "codetie-create": "./index.js"
15 | },
16 | "scripts": {
17 | "lint": "eslint --cache .",
18 | "typecheck": "tsc -p scripts --noEmit && pnpm -r --parallel run typecheck",
19 | "build": "pnpm -r --filter='./packages/*' run build",
20 | "publish": "pnpm -r --filter='./packages/*' run build && npx release-it --git.pushRepo=https://github.com/codetie-ai/codetie.git && tsx scripts/release.ts",
21 | "release": "pnpm run build && npx release-it --git.pushRepo=https://github.com/codetie-ai/codetie.git",
22 | "publish-npm": "tsx scripts/release.ts",
23 | "test-create": "pnpm -F create-codetie run create --debug --cwd /Users/kevin/projects/swift/testing-codetie"
24 | },
25 | "keywords": [
26 | "codetie",
27 | "cli",
28 | "xcode",
29 | "swift",
30 | "project-setup",
31 | "cursor",
32 | "cursor-ai"
33 | ],
34 | "devDependencies": {
35 | "@release-it/bumper": "^6.0.1",
36 | "@release-it/conventional-changelog": "^8.0.2",
37 | "@vitejs/release-scripts": "^1.3.2",
38 | "chalk": "^5.3.0",
39 | "conventional-changelog-cli": "^5.0.0",
40 | "eslint": "^9.10.0",
41 | "execa": "^9.3.1",
42 | "globals": "^15.9.0",
43 | "inquirer": "^10.2.2",
44 | "lint-staged": "^15.2.10",
45 | "mustache": "^4.2.0",
46 | "picocolors": "^1.1.0",
47 | "prompts": "^2.4.2",
48 | "release-it": "^17.6.0",
49 | "rimraf": "^5.0.10",
50 | "semver": "^7.5.4",
51 | "tslib": "^2.7.0",
52 | "tsx": "^4.19.1",
53 | "typescript": "^5.5.3",
54 | "typescript-eslint": "^8.4.0",
55 | "yargs": "^17.7.2"
56 | },
57 | "packageManager": "pnpm@9.9.0",
58 | "pnpm": {
59 | "overrides": {
60 | "codetie": "workspace:*"
61 | }
62 | },
63 | "version": "1.0.9"
64 | }
65 |
--------------------------------------------------------------------------------
/packages/codetie/.npmrc:
--------------------------------------------------------------------------------
1 | shamefully-hoist=true
2 | strict-peer-dependencies=false
--------------------------------------------------------------------------------
/packages/codetie/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true,
4 | "vueIndentScriptAndStyle": false
5 | }
--------------------------------------------------------------------------------
/packages/codetie/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## (2024-09-15)
2 |
3 | * cleanup ([6592721](https://github.com/codetie-ai/codetie/commit/6592721))
4 | * Remove node_modules from the repository ([3446486](https://github.com/codetie-ai/codetie/commit/3446486))
5 | * update ([59fe5e7](https://github.com/codetie-ai/codetie/commit/59fe5e7))
6 | * update packages ([7a60825](https://github.com/codetie-ai/codetie/commit/7a60825))
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/packages/codetie/bin/codetie.mjs:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | import { runMain } from '../dist/index.mjs'
4 |
5 | runMain()
6 |
--------------------------------------------------------------------------------
/packages/codetie/build.config.ts:
--------------------------------------------------------------------------------
1 | import { defineBuildConfig } from 'unbuild'
2 | import type { InputPluginOption } from 'rollup'
3 |
4 | export default defineBuildConfig({
5 | declaration: true,
6 | hooks: {
7 | 'rollup:options'(_, options) {
8 | const plugins = (options.plugins ||= []) as InputPluginOption[]
9 | },
10 | },
11 | rollup: {
12 | inlineDependencies: true,
13 | resolve: {
14 | exportConditions: ['production', 'node'] as any,
15 | },
16 | },
17 | entries: ['src/index'],
18 | // externals: [
19 | // '@nuxt/test-utils',
20 | // 'fsevents',
21 | // 'node:url',
22 | // 'node:buffer',
23 | // 'node:path',
24 | // 'node:child_process',
25 | // 'node:process',
26 | // 'node:path',
27 | // 'node:os',
28 | // ],
29 | })
30 |
--------------------------------------------------------------------------------
/packages/codetie/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "codetie",
3 | "version": "1.0.9",
4 | "description": "Create iOS Apps and Games with Cursor AI & VSCode",
5 | "homepage": "https://codetie.ai",
6 | "author": "Kevin Regenrek",
7 | "repository": {
8 | "type": "git",
9 | "url": "git+https://github.com/codetie-ai/codetie.git",
10 | "directory": "packages/codetie"
11 | },
12 | "type": "module",
13 | "exports": {
14 | ".": "./dist/index.mjs",
15 | "./cli": "./bin/codetie.mjs"
16 | },
17 | "license": "MIT",
18 | "types": "./dist/index.d.ts",
19 | "bin": {
20 | "codetie": "./bin/codetie.mjs"
21 | },
22 | "files": [
23 | "bin",
24 | "dist"
25 | ],
26 | "scripts": {
27 | "release": "release-it",
28 | "build": "unbuild",
29 | "codetie": "node ./bin/codetie.mjs",
30 | "play:generate": "node ./bin/codetie.mjs generate --cwd ./playground",
31 | "play:build": "node ./bin/codetie.mjs build --cwd ./playground",
32 | "play:preview": "node ./bin/codetie.mjs preview --cwd ./playground"
33 | },
34 | "bugs": {
35 | "url": "https://github.com/codetie-ai/codetie/issues"
36 | },
37 | "keywords": [
38 | "codetie",
39 | "cli",
40 | "xcode",
41 | "swift",
42 | "project-setup",
43 | "cursor",
44 | "cursor-ai"
45 | ],
46 | "dependencies": {
47 | "@release-it/conventional-changelog": "^8.0.1",
48 | "c12": "^1.11.2",
49 | "citty": "^0.1.6",
50 | "consola": "^3.2.3",
51 | "destr": "^2.0.3",
52 | "execa": "^9.3.1",
53 | "jiti": "^1.21.6",
54 | "pathe": "^1.1.2",
55 | "release-it": "^17.6.0",
56 | "unbuild": "^2.0.0"
57 | },
58 | "devDependencies": {
59 | "@types/node": "^22.5.5"
60 | },
61 | "packageManager": "pnpm@9.11.0"
62 | }
63 |
--------------------------------------------------------------------------------
/packages/codetie/playground/.gitignore:
--------------------------------------------------------------------------------
1 | *.xcodeproj/
--------------------------------------------------------------------------------
/packages/codetie/playground/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | disabled_rules:
2 | - line_length # Disable if long lines are frequently unavoidable
3 | - trailing_whitespace # Manage this rule based on team preferences
4 | - file_length # Disable for larger files like data models
5 | - cyclomatic_complexity # Disable if complex functions are unavoidable
6 | - force_cast # Be cautious but allow force casting where necessary
7 | - force_unwrapping # Allow force unwrapping for simplicity in some cases
8 | - large_tuple # Manage tuple size based on specific needs
9 | - function_body_length # Disable if longer functions are necessary
10 | - type_body_length # Disable if types naturally grow large
11 |
12 | opt_in_rules:
13 | - empty_count # Prefer `.isEmpty` over counting elements
14 | - closure_end_indentation # Align closing brackets for better readability
15 | - sorted_first_last # Use `.first` and `.last` instead of `.sorted()`
16 | - strict_fileprivate # Enforce using `fileprivate` when appropriate
17 | - redundant_nil_coalescing # Avoid unnecessary nil coalescing
18 |
19 | included:
20 | - Sources
21 | - Tests
22 |
23 | excluded:
24 | - Carthage
25 | - Pods
26 | - .build
27 | - Generated
28 |
29 | reporter: "xcode"
--------------------------------------------------------------------------------
/packages/codetie/playground/codetie.yml:
--------------------------------------------------------------------------------
1 | project_name: "exampleProject"
2 | bundle_id_prefix: "com.example"
3 | deployment_target: "17.0"
4 | xcode_version: "15.3"
5 | swift_version: "5.10.1"
6 | app_version: "1.0.0"
7 | build_number: "1"
--------------------------------------------------------------------------------
/packages/codetie/playground/exampleProject/ContentView.swift:
--------------------------------------------------------------------------------
1 | import Inject
2 | import SwiftUI
3 |
4 | struct ContentView: View {
5 | var body: some View {
6 | VStack {
7 | Image(systemName: "swift")
8 | .resizable()
9 | .aspectRatio(contentMode: .fit)
10 | .frame(width: 100, height: 100)
11 | .foregroundColor(.orange)
12 |
13 | Text("Hello, World!")
14 | .font(.largeTitle)
15 | .fontWeight(.bold)
16 | .padding()
17 | }
18 | .enableInjection()
19 | }
20 | @ObserveInjection var redraw
21 | }
22 |
23 | #Preview {
24 | ContentView()
25 | }
26 |
--------------------------------------------------------------------------------
/packages/codetie/playground/exampleProject/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleVersion
20 | 1
21 | UIApplicationSceneManifest
22 |
23 | UIApplicationSupportsMultipleScenes
24 |
25 |
26 | UILaunchStoryboardName
27 | LaunchScreen
28 |
29 |
30 |
--------------------------------------------------------------------------------
/packages/codetie/playground/exampleProject/debug.xcconfig:
--------------------------------------------------------------------------------
1 | OTHER_LDFLAGS = -Xlinker -interposable
--------------------------------------------------------------------------------
/packages/codetie/playground/exampleProject/exampleProjectApp.swift:
--------------------------------------------------------------------------------
1 | @_exported import Inject
2 | import SwiftUI
3 |
4 | @main
5 | struct hotReload2App: App {
6 | var body: some Scene {
7 | WindowGroup {
8 | ContentView()
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/codetie/playground/project.yml:
--------------------------------------------------------------------------------
1 | name: exampleProject
2 | options:
3 | bundleIdPrefix: com.example
4 | deploymentTarget:
5 | iOS: 17.0
6 | xcodeVersion: "15.3"
7 | generateEmptyDirectories: true
8 | createIntermediateGroups: true
9 |
10 | targets:
11 | exampleProject:
12 | type: application
13 | platform: iOS
14 | sources: [exampleProject]
15 | settings:
16 | base:
17 | SWIFT_VERSION: 5.10.1
18 | ENABLE_TESTABILITY: YES
19 | dependencies:
20 | - package: Inject
21 | - package: HotReloading
22 | info:
23 | path: exampleProject/Info.plist
24 | properties:
25 | CFBundleShortVersionString: "1.0.0"
26 | CFBundleVersion: "1"
27 | UILaunchStoryboardName: LaunchScreen
28 | UIApplicationSceneManifest:
29 | UIApplicationSupportsMultipleScenes: true
30 |
31 | packages:
32 | Inject:
33 | url: https://github.com/krzysztofzablocki/Inject.git
34 | from: "1.2.0"
35 | HotReloading:
36 | url: https://github.com/johnno1962/HotReloading.git
37 | from: "5.0.7"
38 |
39 | schemes:
40 | exampleProject:
41 | build:
42 | targets:
43 | exampleProject: all
44 | run:
45 | config: Debug
46 |
47 | configFiles:
48 | Debug: exampleProject/debug.xcconfig
49 |
50 | configs:
51 | Debug: debug
52 | Release: release
53 |
--------------------------------------------------------------------------------
/packages/codetie/src/commands/build.ts:
--------------------------------------------------------------------------------
1 | import { defineCommand } from "citty";
2 | import { resolve, } from 'pathe'
3 | import { exists } from '../utils/fs'
4 | import { consola } from 'consola'
5 | import { loadConfig } from "c12";
6 | import {$} from 'execa';
7 |
8 | export default defineCommand({
9 | meta: {
10 | name: "build",
11 | description: "Build the project",
12 | },
13 | args: {
14 | cwd: {
15 | type: 'string',
16 | description: 'The directory to build the project in',
17 | }
18 | },
19 | async run(ctx) {
20 | const argsCwd = typeof ctx.args.cwd === 'boolean' ? undefined : ctx.args.cwd
21 | const cwd = resolve(argsCwd || '.')
22 | const configName = 'codetie.yml'
23 | const projectConfigPath = resolve(cwd, configName)
24 |
25 | if (!(await exists(projectConfigPath))) {
26 | consola.error(`Project config ${configName} not found`)
27 | process.exit(1)
28 | }
29 |
30 | const { config } = await loadConfig({ cwd, configFile: configName })
31 |
32 | if (!await exists(resolve(cwd, `${config.project_name}.xcodeproj/project.pbxproj`))) {
33 | consola.error(`${config.project_name}.xcodeproj/project.pbxproj not found. Run 'codetie generate' to generate the project first.`)
34 | process.exit(1)
35 | }
36 |
37 | consola.start(`Building ${config.project_name} for iOS Simulator...`)
38 |
39 | const simulator_name = "iPhone 15 Pro";
40 | const simulator_os = "17.0";
41 | const build_dir = resolve(cwd, 'build')
42 |
43 | try {
44 | await $(`xcodebuild -project ${config.project_name}.xcodeproj -scheme ${config.project_name} -configuration Debug -destination "platform=iOS Simulator,name=${ simulator_name },OS=${ simulator_os }" -derivedDataPath ${ build_dir }`, [], { cwd, shell: true })
45 | const appPath = resolve(cwd, `build/Build/Products/Debug-iphonesimulator/${config.project_name}.app`)
46 | if (!(await exists(appPath))) {
47 | throw new Error(`App bundle not found at expected location: ${appPath}`)
48 | }
49 | consola.success(`${config.project_name} built successfully!`)
50 | } catch (error) {
51 | consola.error(error)
52 | process.exit(1)
53 | }
54 | }
55 | })
--------------------------------------------------------------------------------
/packages/codetie/src/commands/generate.ts:
--------------------------------------------------------------------------------
1 | import { defineCommand } from 'citty'
2 | import { resolve } from 'pathe'
3 | import { consola } from 'consola'
4 | import { $ } from 'execa'
5 |
6 | export default defineCommand({
7 | meta: {
8 | name: 'generate',
9 | description: 'Generate the project',
10 | },
11 | args: {
12 | cwd: {
13 | type: 'string',
14 | description: 'The directory to generate the project in',
15 | },
16 | },
17 | async run(ctx) {
18 | const argsCwd = typeof ctx.args.cwd === 'boolean' ? undefined : ctx.args.cwd
19 | const cwd = resolve(argsCwd || '.')
20 |
21 | try {
22 | const { stdout } = await $`command -v xcodegen &> /dev/null`
23 | if (!stdout) {
24 | throw new Error()
25 | }
26 | } catch (error) {
27 | consola.error(
28 | 'Error: XcodeGen is not installed. Please install XcodeGen before running this script.'
29 | )
30 | consola.box(
31 | 'You can install XcodeGen using Homebrew:\n\nbrew install xcodegen'
32 | )
33 | process.exit(1)
34 | }
35 |
36 | consola.start('Generating Xcode project using XcodeGen...')
37 |
38 | try {
39 | await $(`xcodegen generate`, [], { cwd, shell: true })
40 | consola.success('Xcode project generated successfully.')
41 | } catch (error) {
42 | consola.error(error)
43 | process.exit(1)
44 | }
45 | },
46 | })
47 |
--------------------------------------------------------------------------------
/packages/codetie/src/commands/index.ts:
--------------------------------------------------------------------------------
1 | import type { CommandDef } from 'citty'
2 |
3 | const _rDefault = (r: any) => (r.default || r) as Promise
4 |
5 | export const commands = {
6 | build: () => import('./build').then(_rDefault),
7 | generate: () => import('./generate').then(_rDefault),
8 | preview: () => import('./preview').then(_rDefault),
9 | } as const
10 |
--------------------------------------------------------------------------------
/packages/codetie/src/commands/preview.ts:
--------------------------------------------------------------------------------
1 | import { defineCommand } from 'citty'
2 | import { resolve } from 'pathe'
3 | import { exists } from '../utils/fs'
4 | import { consola } from 'consola'
5 | import { loadConfig } from 'c12'
6 | import { $ } from 'execa'
7 | import { destr } from 'destr'
8 |
9 | interface ISimulator {
10 | dataPath: string
11 | dataPathSize: number
12 | logPath: string
13 | udid: string
14 | isAvailable: boolean
15 | name: string
16 | deviceTypeIdentifier: string
17 | state: string
18 | }
19 |
20 | export default defineCommand({
21 | meta: {
22 | name: 'preview',
23 | description: 'Preview the project',
24 | },
25 | args: {
26 | cwd: {
27 | type: 'string',
28 | description: 'The directory to preview the project from',
29 | },
30 | },
31 | async run(ctx) {
32 | const argsCwd = typeof ctx.args.cwd === 'boolean' ? undefined : ctx.args.cwd
33 | const cwd = resolve(argsCwd || '.')
34 | const configName = 'codetie.yml'
35 | const projectConfigPath = resolve(cwd, configName)
36 |
37 | try {
38 | if (!(await exists(projectConfigPath))) {
39 | consola.error(`Project config ${configName} not found`)
40 | process.exit(1)
41 | }
42 |
43 | const { config } = await loadConfig({ cwd, configFile: configName })
44 | const appPath = resolve(
45 | cwd,
46 | `build/Build/Products/Debug-iphonesimulator/${config.project_name}.app`
47 | )
48 |
49 | if (!(await exists(appPath))) {
50 | throw new Error(`App bundle not found at expected location: ${appPath}`)
51 | }
52 |
53 | // Get the list of installed iOS simulators
54 | const { stdout } = await $(
55 | `xcrun simctl list devices available --json`,
56 | [],
57 | { shell: true }
58 | )
59 | // .pipe(`jq '.devices[] | .[]'`, [], { shell: true})
60 | const deviceObject = destr<{ devices: { [key: string]: ISimulator[] } }>(
61 | `${stdout}`
62 | )
63 | const simulators = Object.values(deviceObject.devices).flat()
64 |
65 | if (simulators.length === 0) {
66 | throw new Error('No iOS simulators found.')
67 | }
68 |
69 | const selectedSimulator = await consola.prompt(
70 | 'Select an iOS simulator to start:',
71 | {
72 | type: 'select',
73 | required: true,
74 | options: simulators.map((simulator) => simulator.name),
75 | }
76 | )
77 |
78 | if (typeof selectedSimulator === 'string') {
79 | process.exit(1)
80 | }
81 |
82 | const simulatorId = (
83 | simulators.find(
84 | (simulator) => simulator.name === selectedSimulator
85 | ) as ISimulator
86 | ).udid
87 |
88 | consola.box(
89 | [
90 | `Using the following settings:\n`,
91 | `Simulator: ${selectedSimulator}`,
92 | `Simulator ID: ${simulatorId}`,
93 | `Project Name: ${config.project_name}`,
94 | `App Bundle ID: ${config.bundle_id_prefix}.${config.project_name}`,
95 | `App Path: ${appPath}`,
96 | ].join('\n')
97 | )
98 |
99 | consola.start(`Starting simulator: ${selectedSimulator}`)
100 |
101 | consola.info('Booting simulator...')
102 | await $(`xcrun simctl boot "${simulatorId}"`, [], { shell: true })
103 |
104 | consola.info('Waiting for simulator to boot...')
105 | while (
106 | (
107 | await $(
108 | `xcrun simctl list devices | grep "${simulatorId}" | grep -o "Booted"`,
109 | [],
110 | { shell: true }
111 | )
112 | ).stdout.trim() !== 'Booted'
113 | ) {
114 | await new Promise((resolve) => setTimeout(resolve, 1000))
115 | }
116 |
117 | consola.info('Opening Simulator...')
118 | await $(`open -a Simulator`, [], { shell: true })
119 |
120 | consola.info('Installing app...')
121 | await $(`xcrun simctl install booted "${appPath}"`, [], { shell: true })
122 |
123 | consola.info('Launching app...')
124 | await $(
125 | `xcrun simctl launch booted "${config.bundle_id_prefix}.${config.project_name}"`,
126 | [],
127 | { shell: true }
128 | )
129 |
130 | consola.success('Done!')
131 | } catch (error) {
132 | consola.error(error)
133 | }
134 | },
135 | })
136 |
--------------------------------------------------------------------------------
/packages/codetie/src/index.ts:
--------------------------------------------------------------------------------
1 | export { main } from './main'
2 | export { runCommand, runMain } from './run'
3 |
--------------------------------------------------------------------------------
/packages/codetie/src/main.ts:
--------------------------------------------------------------------------------
1 | import { defineCommand } from 'citty'
2 | import codetiePkg from '../package.json' assert { type: 'json' }
3 | import { commands } from './commands'
4 |
5 | export const main = defineCommand({
6 | meta: {
7 | name: codetiePkg.name,
8 | version: codetiePkg.version,
9 | description: codetiePkg.description,
10 | },
11 | subCommands: commands,
12 | }) as any
13 |
--------------------------------------------------------------------------------
/packages/codetie/src/run.ts:
--------------------------------------------------------------------------------
1 | import { runCommand as _runCommand, runMain as _runMain } from 'citty'
2 |
3 | import { commands } from './commands'
4 | import { main } from './main'
5 |
6 | export const runMain = () => _runMain(main)
7 |
8 | export async function runCommand(
9 | name: string,
10 | argv: string[] = process.argv.slice(2),
11 | data: { overrides?: Record } = {}
12 | ) {
13 | if (!(name in commands)) {
14 | throw new Error(`Invalid command ${name}`)
15 | }
16 |
17 | return await _runCommand(await commands[name as keyof typeof commands](), {
18 | rawArgs: argv,
19 | data: {
20 | overrides: data.overrides || {},
21 | },
22 | })
23 | }
24 |
--------------------------------------------------------------------------------
/packages/codetie/src/utils/fs.ts:
--------------------------------------------------------------------------------
1 | import { promises as fsp } from 'node:fs'
2 |
3 | // Check if a file exists
4 | export async function exists(path: string) {
5 | try {
6 | await fsp.access(path)
7 | return true
8 | }
9 | catch {
10 | return false
11 | }
12 | }
--------------------------------------------------------------------------------
/packages/codetie/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "esModuleInterop": false,
4 | "allowSyntheticDefaultImports": true,
5 | "skipLibCheck": true,
6 | "target": "ESNext",
7 | "module": "ESNext",
8 | "moduleResolution": "Node",
9 | "strict": true,
10 | "noImplicitAny": true,
11 | "allowJs": true,
12 | "noEmit": true,
13 | "noUnusedLocals": true,
14 | "resolveJsonModule": true,
15 | "types": [
16 | "node",
17 | "./types"
18 | ],
19 | },
20 | "include": [
21 | "src"
22 | ]
23 | }
--------------------------------------------------------------------------------
/packages/codetie/types.d.ts:
--------------------------------------------------------------------------------
1 | export {}
--------------------------------------------------------------------------------
/packages/create-codetie/.gitignore:
--------------------------------------------------------------------------------
1 | playground/
--------------------------------------------------------------------------------
/packages/create-codetie/__buildServer.json:
--------------------------------------------------------------------------------
1 | {
2 | "kind": "xcode",
3 | "project": "fresh1.xcodeproj",
4 | "scheme": "com.fresh1",
5 | "configuration": "Debug",
6 | "sdk": "iphonesimulator",
7 | "destination": {
8 | "platform": "iOS Simulator",
9 | "name": "iPhone 15 Pro",
10 | "OS": "17.0"
11 | },
12 | "build_root": "/Users/kevin/projects/swift/testing-codetie/build",
13 | "build_args": [
14 | "ONLY_ACTIVE_ARCH=YES",
15 | "DEVELOPMENT_TEAM=",
16 | "CODE_SIGN_IDENTITY=",
17 | "CODE_SIGNING_REQUIRED=NO",
18 | "CODE_SIGNING_ALLOWED=NO"
19 | ],
20 | "actions": [
21 | "clean",
22 | "build"
23 | ]
24 | }
--------------------------------------------------------------------------------
/packages/create-codetie/build.config.ts:
--------------------------------------------------------------------------------
1 | import path from 'node:path'
2 | import url from 'node:url'
3 | import { defineBuildConfig } from 'unbuild'
4 |
5 | const __dirname = path.dirname(url.fileURLToPath(import.meta.url))
6 |
7 | export default defineBuildConfig({
8 | entries: ['src/index'],
9 | clean: true,
10 | rollup: {
11 | inlineDependencies: true,
12 | esbuild: {
13 | target: 'node18',
14 | minify: true,
15 | },
16 | },
17 | // alias: {
18 | // we can always use non-transpiled code since we support node 18+
19 | // prompts: 'prompts/lib/index.js',
20 | // },
21 | hooks: {
22 | 'rollup:options'(_ctx, options) {
23 | options.plugins = [
24 | options.plugins
25 | ]
26 | },
27 | },
28 | })
--------------------------------------------------------------------------------
/packages/create-codetie/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | import './dist/index.mjs'
--------------------------------------------------------------------------------
/packages/create-codetie/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "create-codetie",
3 | "version": "1.0.9",
4 | "description": "Create a new codetie project",
5 | "main": "index.js",
6 | "bin": {
7 | "create-codetie": "index.js"
8 | },
9 | "type": "module",
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/codetie-ai/codetie.git",
13 | "directory": "packages/create-codetie"
14 | },
15 | "bugs": {
16 | "url": "https://github.com/codetie-ai/codetie/issues"
17 | },
18 | "license": "MIT",
19 | "scripts": {
20 | "create": "tsx src/index.ts",
21 | "play:clean": "rm -rf playground",
22 | "dev": "unbuild --stub",
23 | "build": "unbuild",
24 | "typecheck": "tsc --noEmit",
25 | "prepublishOnly": "npm run build"
26 | },
27 | "keywords": [
28 | "codetie",
29 | "cli",
30 | "xcode",
31 | "swift",
32 | "project-setup",
33 | "cursor",
34 | "cursor-ai"
35 | ],
36 | "dependencies": {
37 | "@types/fs-extra": "^11.0.4",
38 | "@types/mustache": "^4.2.5",
39 | "citty": "^0.1.6",
40 | "codetie": "^1.0.8",
41 | "consola": "^3.2.3",
42 | "fs-extra": "^11.2.0",
43 | "mustache": "^4.2.0",
44 | "pathe": "^1.1.2",
45 | "scule": "^1.3.0",
46 | "unbuild": "^2.0.0"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/packages/create-codetie/src/index.ts:
--------------------------------------------------------------------------------
1 | import fs from "fs-extra";
2 | import { runMain, defineCommand } from "citty";
3 | import { consola } from "consola";
4 | import { resolve, dirname } from "pathe";
5 | import { fileURLToPath } from "node:url";
6 | import {
7 | exists,
8 | mkDir,
9 | isEmpty,
10 | readDir,
11 | writeFile,
12 | readFile,
13 | copyFile,
14 | rename,
15 | deleteFile,
16 | } from "./utils/fs";
17 | import { initPrompts, installDependenciesPrompts } from "./prompts";
18 | import Mustache from "mustache";
19 |
20 | const __filename = fileURLToPath(import.meta.url);
21 | const __dirname = dirname(__filename);
22 | const templateDir = resolve(__dirname, "../template");
23 |
24 | const renameFiles: Record = {
25 | _gitignore: ".gitignore",
26 | };
27 |
28 | const main = defineCommand({
29 | args: {
30 | cwd: {
31 | type: "string",
32 | description: "The directory to create the project in",
33 | },
34 | },
35 | async run(ctx) {
36 | consola.log(templateDir);
37 | const argsCwd =
38 | ctx.args._[0] ?? (typeof ctx.args.cwd === "string" ? ctx.args.cwd : ".");
39 | const cwd = resolve(argsCwd);
40 |
41 | try {
42 | // 1. prompts
43 | const {
44 | projectName,
45 | bundleIdPrefix,
46 | deploymentTarget,
47 | xcodeVersion,
48 | swiftVersion,
49 | appVersion,
50 | buildNumber,
51 | } = await initPrompts();
52 |
53 | // 2. check if cwd is empty
54 | const root = resolve(cwd, projectName);
55 | if (exists(root)) {
56 | if (!isEmpty(root)) {
57 | throw new Error(
58 | `Directory ${root} is not empty. Please choose a different directory or empty it.`
59 | );
60 | }
61 | } else {
62 | mkDir(root);
63 | }
64 |
65 | // 3. copy template
66 | const templateVariables = {
67 | PROJECT_NAME: projectName,
68 | BUNDLE_ID_PREFIX: bundleIdPrefix,
69 | DEPLOYMENT_TARGET: deploymentTarget,
70 | XCODE_VERSION: xcodeVersion,
71 | SWIFT_VERSION: swiftVersion,
72 | APP_VERSION: appVersion,
73 | BUILD_NUMBER: buildNumber,
74 | };
75 |
76 | const files = readDir(templateDir);
77 | for (const file of files) {
78 | const srcPath = resolve(file.path, file.name);
79 | const targetParentPath = resolve(
80 | root,
81 | file.path.replace(new RegExp(`^${templateDir}\\/?`, "g"), "")
82 | );
83 | const destPath = resolve(
84 | targetParentPath,
85 | renameFiles[file.name] ?? file.name
86 | );
87 | const stat = fs.statSync(srcPath);
88 | if (stat.isDirectory()) {
89 | mkDir(destPath);
90 | } else if (file.name.endsWith(".mustache")) {
91 | if (exists(srcPath)) {
92 | const content = readFile(srcPath);
93 | const rendered = Mustache.render(content, templateVariables);
94 | writeFile(destPath.replace(/\.mustache$/g, ""), rendered);
95 | }
96 | } else {
97 | copyFile(srcPath, destPath);
98 | }
99 | }
100 | // 4. rename project entry file and content
101 | const oldMainSwift = resolve(root, "_project", "_main.swift");
102 | const newMainSwift = resolve(root, "_project", `${projectName}App.swift`);
103 | if (exists(oldMainSwift)) {
104 | const content = readFile(oldMainSwift).replace(
105 | "HelloWorldApp",
106 | `${projectName}App`
107 | );
108 | writeFile(newMainSwift, content);
109 | deleteFile(oldMainSwift);
110 | }
111 | // 5. rename _project folder
112 | rename(resolve(root, "_project"), resolve(root, projectName));
113 |
114 | // 6. generate codetie.yml
115 | const codetieConfig = {
116 | project_name: projectName,
117 | bundle_id_prefix: bundleIdPrefix,
118 | deployment_target: deploymentTarget,
119 | xcode_version: xcodeVersion,
120 | swift_version: swiftVersion,
121 | app_version: appVersion,
122 | build_number: buildNumber,
123 | };
124 | writeFile(
125 | resolve(root, "codetie.yml"),
126 | JSON.stringify(codetieConfig, null, 2)
127 | );
128 |
129 | // 6. install dependencies
130 | await installDependenciesPrompts(root);
131 |
132 | consola.box("Project setup complete! Happy coding! 🎈");
133 | } catch (error) {
134 | consola.error(error);
135 | process.exit(1);
136 | }
137 | },
138 | });
139 |
140 | runMain(main);
141 |
--------------------------------------------------------------------------------
/packages/create-codetie/src/prompts/index.ts:
--------------------------------------------------------------------------------
1 | export { initPrompts } from "./init";
2 | export { installDependenciesPrompts } from "./installDependencies";
3 |
--------------------------------------------------------------------------------
/packages/create-codetie/src/prompts/init.ts:
--------------------------------------------------------------------------------
1 | import { consola } from "consola";
2 | import { camelCase } from "scule";
3 |
4 | async function prompt(
5 | message: string,
6 | options: any,
7 | required: boolean = true,
8 | errorMessage: string = "Field is required"
9 | ) {
10 | const result = await consola.prompt(message, options);
11 | if (typeof result === "symbol") {
12 | process.exit(1);
13 | }
14 | if (required && (result || "").trim().length === 0) {
15 | throw new Error(errorMessage);
16 | }
17 | return (result || "").trim();
18 | }
19 |
20 | export async function initPrompts() {
21 | const projectName = camelCase(
22 | (await prompt(
23 | "Enter project name:",
24 | { type: "text", placeholder: "codetieProject" },
25 | true,
26 | "Project name is required"
27 | )) || "codetieProject"
28 | );
29 | const bundleIdPrefix =
30 | (await prompt(
31 | "Enter Bundle ID Prefix:",
32 | { type: "text", placeholder: "com.example" },
33 | false,
34 | "Bundle ID Prefix is required"
35 | )) || "com.example";
36 | const deploymentTarget =
37 | (await prompt(
38 | "Enter Deployment Target iOS Version:",
39 | { type: "text", placeholder: "17.0" },
40 | false,
41 | "Deployment Target is required"
42 | )) || "17.0";
43 | const xcodeVersion =
44 | (await prompt(
45 | "Enter Xcode Version:",
46 | { type: "text", placeholder: "15.3" },
47 | false,
48 | "Xcode Version is required"
49 | )) || "15.3";
50 | const swiftVersion =
51 | (await prompt(
52 | "Enter Swift Version:",
53 | { type: "text", placeholder: "5.10.1" },
54 | false,
55 | "Swift Version is required"
56 | )) || "5.10.1";
57 | const appVersion =
58 | (await prompt(
59 | "Enter App Version:",
60 | { type: "text", placeholder: "1.0.0" },
61 | false,
62 | "App Version is required"
63 | )) || "1.0.0";
64 | const buildNumber =
65 | (await prompt(
66 | "Enter Build Number:",
67 | { type: "text", placeholder: "1" },
68 | false,
69 | "Build Number is required"
70 | )) || "1";
71 | return {
72 | projectName,
73 | bundleIdPrefix,
74 | deploymentTarget,
75 | xcodeVersion,
76 | swiftVersion,
77 | appVersion,
78 | buildNumber,
79 | };
80 | }
81 |
--------------------------------------------------------------------------------
/packages/create-codetie/src/prompts/installDependencies.ts:
--------------------------------------------------------------------------------
1 | import { consola } from "consola";
2 | import { runCommand } from "codetie";
3 |
4 | export async function installDependenciesPrompts(cwd: string) {
5 | const generateProject = await consola.prompt(
6 | "Do you want to generate the Xcode project now?",
7 | {
8 | type: "confirm",
9 | }
10 | );
11 | if (typeof generateProject === "symbol") {
12 | process.exit(1);
13 | }
14 | if (generateProject) {
15 | await runCommand("generate", ["", "", "--cwd", cwd]);
16 |
17 | const startSimulator = await consola.prompt(
18 | "Do you want to start the project with the simulator now?",
19 | {
20 | type: "confirm",
21 | }
22 | );
23 | if (typeof startSimulator === "symbol") {
24 | process.exit(1);
25 | }
26 | if (startSimulator) {
27 | await runCommand("build", ["", "", "--cwd", cwd]);
28 | await runCommand("preview", ["", "", "--cwd", cwd]);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/packages/create-codetie/src/utils/fs.ts:
--------------------------------------------------------------------------------
1 | import fs from "fs-extra";
2 | import { resolve } from "pathe";
3 |
4 | export function exists(path: string) {
5 | return fs.existsSync(path);
6 | }
7 |
8 | export function isEmpty(path: string) {
9 | return fs.readdirSync(path).length === 0;
10 | }
11 |
12 | export function mkDir(path: string) {
13 | return fs.mkdirSync(path, { recursive: true });
14 | }
15 |
16 | export function readDir(path: string) {
17 | return fs.readdirSync(path, { withFileTypes: true, recursive: true });
18 | }
19 |
20 | export function copyDir(src: string, dest: string) {
21 | mkDir(dest);
22 | for (const file of fs.readdirSync(src)) {
23 | const srcFile = resolve(src, file);
24 | const destFile = resolve(dest, file);
25 | copy(srcFile, destFile);
26 | }
27 | }
28 |
29 | export function writeFile(path: string, content: string) {
30 | const destDir = resolve(path, "..");
31 | fs.ensureDirSync(destDir);
32 | fs.writeFileSync(path, content);
33 | }
34 |
35 | export function copyFile(src: string, dest: string) {
36 | const destDir = resolve(dest, "..");
37 | fs.ensureDirSync(destDir);
38 | fs.copyFileSync(src, dest);
39 | }
40 |
41 | export function copy(src: string, dest: string) {
42 | const stat = fs.statSync(src);
43 | if (stat.isDirectory()) {
44 | copyDir(src, dest);
45 | } else {
46 | copyFile(src, dest);
47 | }
48 | }
49 |
50 | export function readFile(path: string) {
51 | return fs.readFileSync(path, "utf-8");
52 | }
53 |
54 | export function rename(oldPath: string, newPath: string) {
55 | return fs.renameSync(oldPath, newPath);
56 | }
57 |
58 | export function deleteFile(path: string) {
59 | return fs.unlinkSync(path);
60 | }
61 |
62 | export function deleteDir(path: string) {
63 | return fs.rmdirSync(path);
64 | }
65 |
--------------------------------------------------------------------------------
/packages/create-codetie/template/.cursorrules:
--------------------------------------------------------------------------------
1 | you are an expert in coding with swift, swift ui. you always write maintainable code and clean code.
2 | focus on latest august, september 2024 version of the documentation and features.
3 |
4 | SwiftUIProject structure:
5 | All code is placed in the "MyZunderApp" folder. This folder includes all main entry files like App.swift, AppConfig.swift, the we have a "Views" group with different views, "Shared" for reusable components and modifiers. It includes "Models" for data models, "Services" with "Network" for networking and "Persistence" for data storage, and "Utils" for extensions, constants, and helpers. The "Resources" folder holds "Assets" for images and colors, "Localization" for localized strings, and "Fonts" for custom fonts. Lastly, the "Tests" folder includes "UnitTests" for unit testing and "UITests" for UI testing.
6 |
7 | GENERAL RULES:
8 | - your descriptions should be short and concise.
9 | - don't remove any comments.
10 |
11 | SWIFTUI DESIGN RULES:
12 | - Use Built-in Components: Utilize SwiftUI's native UI elements like List, NavigationView, TabView, and SF Symbols for a polished, iOS-consistent look.
13 | - Master Layout Tools: Employ VStack, HStack, ZStack, Spacer, and Padding for responsive designs; use LazyVGrid and LazyHGrid for grids; GeometryReader for dynamic layouts.
14 | - Add Visual Flair: Enhance UIs with shadows, gradients, blurs, custom shapes, and animations using the .animation() modifier for smooth transitions.
15 | Design for Interaction: Incorporate gestures (swipes, long presses), haptic feedback, clear navigation, and responsive elements to improve user engagement and satisfaction.
--------------------------------------------------------------------------------
/packages/create-codetie/template/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | disabled_rules:
2 | - line_length # Disable if long lines are frequently unavoidable
3 | - trailing_whitespace # Manage this rule based on team preferences
4 | - file_length # Disable for larger files like data models
5 | - cyclomatic_complexity # Disable if complex functions are unavoidable
6 | - force_cast # Be cautious but allow force casting where necessary
7 | - force_unwrapping # Allow force unwrapping for simplicity in some cases
8 | - large_tuple # Manage tuple size based on specific needs
9 | - function_body_length # Disable if longer functions are necessary
10 | - type_body_length # Disable if types naturally grow large
11 |
12 | opt_in_rules:
13 | - empty_count # Prefer `.isEmpty` over counting elements
14 | - closure_end_indentation # Align closing brackets for better readability
15 | - sorted_first_last # Use `.first` and `.last` instead of `.sorted()`
16 | - strict_fileprivate # Enforce using `fileprivate` when appropriate
17 | - redundant_nil_coalescing # Avoid unnecessary nil coalescing
18 |
19 | included:
20 | - Sources
21 | - Tests
22 |
23 | excluded:
24 | - Carthage
25 | - Pods
26 | - .build
27 | - Generated
28 |
29 | reporter: "xcode"
--------------------------------------------------------------------------------
/packages/create-codetie/template/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "sswg.swift-lang",
4 | "vknabel.vscode-swiftlint"
5 | ]
6 | }
--------------------------------------------------------------------------------
/packages/create-codetie/template/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.codeActionsOnSave": {
3 | "source.fixAll": "explicit"
4 | },
5 | "[swift]": {
6 | "editor.defaultFormatter": "vknabel.vscode-swiftlint"
7 | },
8 | "swiftlint.enable": true,
9 | "swiftlint.configSearchPaths": [
10 | "./.swiftlint.yml"
11 | ]
12 | }
--------------------------------------------------------------------------------
/packages/create-codetie/template/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "Generate project",
6 | "type": "shell",
7 | "command": "npx codetie generate",
8 | "problemMatcher": []
9 | },
10 | {
11 | "label": "Build project",
12 | "type": "shell",
13 | "command": "npx codetie build",
14 | "problemMatcher": []
15 | },
16 | {
17 | "label": "Preview project",
18 | "type": "shell",
19 | "command": "npx codetie preview",
20 | "problemMatcher": []
21 | }
22 | ]
23 | }
--------------------------------------------------------------------------------
/packages/create-codetie/template/README.md:
--------------------------------------------------------------------------------
1 | # Codetie Starter
2 |
3 | This is a starter template for creating iOS apps and games with Cursor AI & VSCode.
4 |
--------------------------------------------------------------------------------
/packages/create-codetie/template/_gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 | ## User settings
5 | xcuserdata/
6 |
7 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
8 | *.xcscmblueprint
9 | *.xccheckout
10 |
11 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
12 | build/
13 | DerivedData/
14 | *.moved-aside
15 | *.pbxuser
16 | !default.pbxuser
17 | *.mode1v3
18 | !default.mode1v3
19 | *.mode2v3
20 | !default.mode2v3
21 | *.perspectivev3
22 | !default.perspectivev3
23 |
24 | ## Obj-C/Swift specific
25 | *.hmap
26 |
27 | ## App packaging
28 | *.ipa
29 | *.dSYM.zip
30 | *.dSYM
31 |
32 | ## Playgrounds
33 | timeline.xctimeline
34 | playground.xcworkspace
35 |
36 | # Swift Package Manager
37 | #
38 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
39 | # Packages/
40 | # Package.pins
41 | # Package.resolved
42 | # *.xcodeproj
43 | #
44 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
45 | # hence it is not needed unless you have added a package configuration file to your project
46 | # .swiftpm
47 |
48 | .build/
49 |
50 | # CocoaPods
51 | #
52 | # We recommend against adding the Pods directory to your .gitignore. However
53 | # you should judge for yourself, the pros and cons are mentioned at:
54 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
55 | #
56 | # Pods/
57 | #
58 | # Add this line if you want to avoid checking in source code from the Xcode workspace
59 | # *.xcworkspace
60 |
61 | # Carthage
62 | #
63 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
64 | # Carthage/Checkouts
65 |
66 | Carthage/Build/
67 |
68 | # Accio dependency management
69 | Dependencies/
70 | .accio/
71 |
72 | # fastlane
73 | #
74 | # It is recommended to not store the screenshots in the git repo.
75 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
76 | # For more information about the recommended setup visit:
77 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
78 |
79 | fastlane/report.xml
80 | fastlane/Preview.html
81 | fastlane/screenshots/**/*.png
82 | fastlane/test_output
83 |
84 | # Code Injection
85 | #
86 | # After new code Injection tools there's a generated folder /iOSInjectionProject
87 | # https://github.com/johnno1962/injectionforxcode
88 |
89 | iOSInjectionProject/
90 |
91 | # macOS
92 | .DS_Store
93 |
94 | # SwiftUI Preview Content
95 | **/Preview Content
96 |
97 |
98 | node_modules
99 | dist
100 | .DS_Store
101 | .history
102 | .vercel
103 | .idea
104 | .env
105 | .data
106 |
--------------------------------------------------------------------------------
/packages/create-codetie/template/_project/ContentView.swift:
--------------------------------------------------------------------------------
1 | import Inject
2 | import SwiftUI
3 |
4 | struct ContentView: View {
5 | var body: some View {
6 | VStack {
7 | Image(systemName: "swift")
8 | .resizable()
9 | .aspectRatio(contentMode: .fit)
10 | .frame(width: 100, height: 100)
11 | .foregroundColor(.orange)
12 |
13 | Text("Hello, World!")
14 | .font(.largeTitle)
15 | .fontWeight(.bold)
16 | .padding()
17 | }
18 | .enableInjection()
19 | }
20 | @ObserveInjection var redraw
21 | }
22 |
23 | #Preview {
24 | ContentView()
25 | }
26 |
--------------------------------------------------------------------------------
/packages/create-codetie/template/_project/_main.swift:
--------------------------------------------------------------------------------
1 | @_exported import Inject
2 | import SwiftUI
3 |
4 | @main
5 | struct HelloWorldApp: App {
6 | var body: some Scene {
7 | WindowGroup {
8 | ContentView()
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/create-codetie/template/_project/debug.xcconfig:
--------------------------------------------------------------------------------
1 | OTHER_LDFLAGS = -Xlinker -interposable
--------------------------------------------------------------------------------
/packages/create-codetie/template/project.yml.mustache:
--------------------------------------------------------------------------------
1 | name: {{PROJECT_NAME}}
2 | options:
3 | bundleIdPrefix: {{BUNDLE_ID_PREFIX}}
4 | deploymentTarget:
5 | iOS: {{DEPLOYMENT_TARGET}}
6 | xcodeVersion: "{{XCODE_VERSION}}"
7 | generateEmptyDirectories: true
8 | createIntermediateGroups: true
9 |
10 | targets:
11 | {{PROJECT_NAME}}:
12 | type: application
13 | platform: iOS
14 | sources: [{{PROJECT_NAME}}]
15 | settings:
16 | base:
17 | SWIFT_VERSION: {{SWIFT_VERSION}}
18 | ENABLE_TESTABILITY: YES
19 | dependencies:
20 | - package: Inject
21 | info:
22 | path: {{PROJECT_NAME}}/Info.plist
23 | properties:
24 | CFBundleShortVersionString: "{{APP_VERSION}}"
25 | CFBundleVersion: "{{BUILD_NUMBER}}"
26 | UILaunchStoryboardName: LaunchScreen
27 | UIApplicationSceneManifest:
28 | UIApplicationSupportsMultipleScenes: true
29 |
30 | packages:
31 | Inject:
32 | url: https://github.com/krzysztofzablocki/Inject.git
33 | from: "1.2.0"
34 |
35 | schemes:
36 | {{PROJECT_NAME}}:
37 | build:
38 | targets:
39 | {{PROJECT_NAME}}: all
40 | run:
41 | config: Debug
42 |
43 | configFiles:
44 | Debug: {{PROJECT_NAME}}/debug.xcconfig
45 |
46 | configs:
47 | Debug: debug
48 | Release: release
--------------------------------------------------------------------------------
/packages/create-codetie/template/scripts/build_project.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Get the project name from the first argument
4 | PROJECT_NAME=$1
5 |
6 | # Remove .xcodeproj extension if present
7 | PROJECT_NAME=${PROJECT_NAME%.xcodeproj}
8 |
9 | # Set the output directory
10 | BUILD_DIR="$(pwd)/build"
11 |
12 | # Set simulator details
13 | SIMULATOR_NAME="iPhone 15 Pro"
14 | SIMULATOR_OS="17.0" # Updated to a more likely iOS version
15 |
16 | # Get the latest iOS runtime
17 | RUNTIME=$(xcrun simctl list runtimes | grep iOS | tail -1 | awk '{print $NF}')
18 |
19 | # Get the device type
20 | DEVICE_TYPE=$(xcrun simctl list devicetypes | grep "$DEVICE" | tail -1 | awk '{print $NF}')
21 |
22 | if [ -z "$RUNTIME" ] || [ -z "$DEVICE_TYPE" ]; then
23 | echo "Could not determine runtime or device type for $DEVICE"
24 | exit 1
25 | fi
26 |
27 | # Ensure the project file exists
28 | if [ ! -f "${PROJECT_NAME}.xcodeproj/project.pbxproj" ]; then
29 | echo "Error: ${PROJECT_NAME}.xcodeproj not found in the current directory."
30 | exit 1
31 | fi
32 |
33 |
34 | # Check if the simulator already exists
35 | if ! xcrun simctl list devices | grep -q "$SIMULATOR_NAME"; then
36 | echo "Creating $SIMULATOR_NAME simulator..."
37 | xcrun simctl create "$SIMULATOR_NAME" "$SIMULATOR_NAME" "$RUNTIME"
38 | else
39 | echo "$SIMULATOR_NAME simulator already exists."
40 | fi
41 |
42 | # Build the project for iOS Simulator
43 | # echo "Building ${PROJECT_NAME} for iOS Simulator..."
44 | # xcodebuild -project "${PROJECT_NAME}.xcodeproj" -scheme "${PROJECT_NAME}" \
45 | # -configuration Debug \
46 | # -sdk iphonesimulator \
47 | # -destination "platform=iOS Simulator,name=${SIMULATOR_NAME},OS=${SIMULATOR_OS}" \
48 | # -derivedDataPath "${BUILD_DIR}" \
49 | # -allowProvisioningUpdates \
50 | # CODE_SIGN_STYLE=Automatic \
51 | # DEVELOPMENT_TEAM="3G79D9D34R" \
52 | # clean build
53 |
54 | xcodebuild -project "${PROJECT_NAME}.xcodeproj" -scheme "${PROJECT_NAME}" \
55 | -configuration Debug \
56 | -destination "platform=iOS Simulator,name=${SIMULATOR_NAME}" \
57 | -derivedDataPath "${BUILD_DIR}" \
58 | ONLY_ACTIVE_ARCH=YES \
59 | CODE_SIGN_IDENTITY="" \
60 | CODE_SIGNING_REQUIRED=NO \
61 | CODE_SIGNING_ALLOWED=NO \
62 | clean build
63 |
64 | # xcodebuild -project "${PROJECT_NAME}.xcodeproj" -scheme "${PROJECT_NAME}" \
65 | # -configuration Debug \
66 | # -destination "platform=iOS Simulator,name=${SIMULATOR_NAME},OS=${SIMULATOR_OS}" \
67 | # -derivedDataPath "${BUILD_DIR}" \
68 | # ONLY_ACTIVE_ARCH=YES \
69 | # CODE_SIGN_IDENTITY="" \
70 | # CODE_SIGNING_REQUIRED=NO \
71 | # CODE_SIGNING_ALLOWED=NO \
72 | # clean build
73 |
74 | # xcodebuild -project "${PROJECT_NAME}.xcodeproj" -scheme "${PROJECT_NAME}" \
75 | # -configuration Debug \
76 | # -destination "platform=iOS Simulator,name=iPhone 14,OS=latest" \
77 | # -derivedDataPath "${BUILD_DIR}" \
78 | # -verbose \
79 | # ONLY_ACTIVE_ARCH=YES \
80 | # DEVELOPMENT_TEAM="" \
81 | # CODE_SIGN_IDENTITY="" \
82 | # CODE_SIGNING_REQUIRED=NO \
83 | # CODE_SIGNING_ALLOWED=NO \
84 | # clean build
85 |
86 | # xcodebuild -project "app_w_xcode.xcodeproj" -scheme "app_w_xcode" \
87 | # -configuration Debug \
88 | # -destination "platform=iOS Simulator,name=iPhone 15" \
89 | # -derivedDataPath "/Users/kevin/projects/swift/testing-codetie/app_w_xcode/build" \
90 | # -verbose \
91 | # ONLY_ACTIVE_ARCH=YES \
92 | # DEVELOPMENT_TEAM="" \
93 | # CODE_SIGN_IDENTITY="" \
94 | # CODE_SIGNING_REQUIRED=NO \
95 | # CODE_SIGNING_ALLOWED=NO \
96 | # clean build
97 |
98 | # CONFIGURATION_BUILD_DIR="${BUILD_DIR}/Build/Products/Debug-iphonesimulator" \
99 |
100 | # Check if the build was successful
101 | if [ $? -eq 0 ]; then
102 | APP_PATH="${BUILD_DIR}/Build/Products/Debug-iphonesimulator/${PROJECT_NAME}.app"
103 | if [ -d "$APP_PATH" ]; then
104 | echo "✅ Project built successfully!"
105 | echo "App bundle location: $APP_PATH"
106 | # Export the APP_PATH for use in run_simulator.sh
107 | echo "export APP_PATH=\"$APP_PATH\"" > build_output.sh
108 | else
109 | echo "❌ Build succeeded, but app bundle not found at expected location."
110 | exit 1
111 | fi
112 | else
113 | echo "❌ Project build failed."
114 | exit 1
115 | fi
--------------------------------------------------------------------------------
/packages/create-codetie/template/scripts/build_project_server.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Get the project name from the first argument
4 | PROJECT_NAME=$1
5 |
6 | # Remove .xcodeproj extension if present
7 | PROJECT_NAME=${PROJECT_NAME%.xcodeproj}
8 |
9 | # Set the output directory
10 | BUILD_DIR="$(pwd)/build"
11 |
12 | # Ensure the project file exists
13 | if [ ! -f "${PROJECT_NAME}.xcodeproj/project.pbxproj" ]; then
14 | echo "Error: ${PROJECT_NAME}.xcodeproj not found in the current directory."
15 | exit 1
16 | fi
17 |
18 | # Configure xcode-build-server
19 | xcode-build-server config -project "${PROJECT_NAME}.xcodeproj" -scheme "${PROJECT_NAME}"
20 |
21 | # Build the project for iOS Simulator and generate build log
22 | xcodebuild -project "${PROJECT_NAME}.xcodeproj" -scheme "${PROJECT_NAME}" \
23 | -configuration Debug \
24 | -destination "platform=iOS Simulator,name=iPhone 14,OS=latest" \
25 | -derivedDataPath "${BUILD_DIR}" \
26 | -resultBundlePath .bundle \
27 | clean build | tee build.log
28 |
29 | # Parse the build log with xcode-build-server
30 | xcode-build-server parse -a build.log
31 |
32 | # Check if the build was successful
33 | if [ $? -eq 0 ]; then
34 | APP_PATH="${BUILD_DIR}/Build/Products/Debug-iphonesimulator/${PROJECT_NAME}.app"
35 | if [ -d "$APP_PATH" ]; then
36 | echo "✅ Project built successfully!"
37 | echo "App bundle location: $APP_PATH"
38 | # Export the APP_PATH for use in run_simulator.sh
39 | echo "export APP_PATH=\"$APP_PATH\"" > build_output.sh
40 | else
41 | echo "❌ Build succeeded, but app bundle not found at expected location."
42 | exit 1
43 | fi
44 | else
45 | echo "❌ Project build failed."
46 | exit 1
47 | fi
--------------------------------------------------------------------------------
/packages/create-codetie/template/scripts/parse_build_log.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codetie-ai/codetie/0603d6d4cbcd4d557ed14b6bfae0846447ec6b23/packages/create-codetie/template/scripts/parse_build_log.py
--------------------------------------------------------------------------------
/packages/create-codetie/template/scripts/run_simulator.sh.mustache:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Get the simulator name from the argument, or use the default if not provided
4 | SIMULATOR_NAME="${1:-iPhone 14}"
5 |
6 | # Get the simulator ID
7 | SIMULATOR_ID=$(xcrun simctl list devices | grep "$SIMULATOR_NAME" | grep -E -o -i "([0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12})" | head -n 1)
8 |
9 | # Debugging: Print the retrieved SIMULATOR_ID
10 | # echo "Retrieved SIMULATOR_ID: $SIMULATOR_ID"
11 |
12 | if [ -z "$SIMULATOR_ID" ]; then
13 | echo "Error: Simulator '$SIMULATOR_NAME' not found."
14 | exit 1
15 | fi
16 |
17 | # Set variables
18 | SCHEME_NAME="{{PROJECT_NAME}}" # Use environment variable or default
19 | APP_BUNDLE_ID="{{BUNDLE_ID_PREFIX}}.$SCHEME_NAME" # Use environment variable or generate from SCHEME_NAME
20 | APP_PATH="$(pwd)/build/Build/Products/Debug-iphonesimulator/$SCHEME_NAME.app"
21 |
22 | # Print the variables for verification
23 | echo "Using the following settings:"
24 | echo "SIMULATOR_NAME: $SIMULATOR_NAME"
25 | echo "SIMULATOR_ID: $SIMULATOR_ID"
26 | echo "SCHEME_NAME: $SCHEME_NAME"
27 | echo "APP_BUNDLE_ID: $APP_BUNDLE_ID"
28 | echo "APP_PATH: $APP_PATH"
29 |
30 | # Ensure the simulator is shutdown before booting
31 | echo "Shutting down simulator if it's running..."
32 | xcrun simctl shutdown "$SIMULATOR_ID"
33 |
34 | # Boot the simulator
35 | echo "Booting simulator..."
36 | xcrun simctl boot "$SIMULATOR_ID" || { echo "Error: Failed to boot simulator with ID $SIMULATOR_ID"; exit 1; }
37 |
38 | # Wait until the simulator is fully booted
39 | echo "Waiting for simulator to boot..."
40 | while [[ "$(xcrun simctl list devices | grep "$SIMULATOR_ID" | grep -o "Booted")" != "Booted" ]]; do
41 | sleep 1
42 | done
43 |
44 | # Open Simulator
45 | echo "Opening Simulator..."
46 | open -a Simulator
47 |
48 | if [ -z "$APP_PATH" ]; then
49 | echo "Error: Built app not found."
50 | exit 1
51 | fi
52 |
53 | # Install the app
54 | echo "Installing app..."
55 | xcrun simctl install booted "$APP_PATH"
56 |
57 | # Launch the app
58 | echo "Launching app..."
59 | xcrun simctl launch booted "$APP_BUNDLE_ID"
60 |
61 | echo "Done!"
--------------------------------------------------------------------------------
/packages/create-codetie/template/simulators.yml:
--------------------------------------------------------------------------------
1 | defaultAlways: false
2 | default: "iPhone 14"
3 | simulators:
4 | - "iPhone 14"
5 | - "iPhone 14 Pro"
6 | - "iPhone 14 Pro Max"
7 | - "iPhone SE (3rd generation)"
8 | - "iPad Air (5th generation)"
9 | - "iPad Pro (11-inch) (4th generation)"
10 | - "iPad Pro (12.9-inch) (6th generation)"
--------------------------------------------------------------------------------
/packages/create-codetie/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": [
3 | "build.config.ts",
4 | "src",
5 | "__tests__"
6 | ],
7 | "compilerOptions": {
8 | "outDir": "dist",
9 | "target": "ES2022",
10 | "module": "ES2020",
11 | "moduleResolution": "bundler",
12 | "strict": true,
13 | "skipLibCheck": true,
14 | "declaration": false,
15 | "sourceMap": false,
16 | "noUnusedLocals": true,
17 | "esModuleInterop": true
18 | }
19 | }
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - 'packages/*'
--------------------------------------------------------------------------------
/scripts/__release.ts:
--------------------------------------------------------------------------------
1 | import { release } from '@vitejs/release-scripts'
2 | import colors from 'picocolors'
3 | import { logRecentCommits, run, updateTemplateVersions } from './releaseUtils'
4 | import extendCommitHash from './extendCommitHash'
5 |
6 | release({
7 | repo: 'codetie',
8 | packages: ['codetie', 'create-codetie'],
9 | toTag: (pkg, version) =>
10 | pkg === 'codetie' ? `v${version}` : `${pkg}@${version}`,
11 | logChangelog: (pkg) => logRecentCommits(pkg),
12 | generateChangelog: async (pkgName) => {
13 | if (pkgName === 'create-codetie') await updateTemplateVersions()
14 |
15 | console.log(colors.cyan('\nGenerating changelog...'))
16 | const changelogArgs = [
17 | 'conventional-changelog',
18 | '-p',
19 | 'angular',
20 | '-i',
21 | 'CHANGELOG.md',
22 | '-s',
23 | '--commit-path',
24 | '.',
25 | ]
26 | if (pkgName !== 'codetie') changelogArgs.push('--lerna-package', pkgName)
27 | await run('npx', changelogArgs, { cwd: `packages/${pkgName}` })
28 | // conventional-changelog generates links with short commit hashes, extend them to full hashes
29 | extendCommitHash(`packages/${pkgName}/CHANGELOG.md`)
30 | },
31 | })
--------------------------------------------------------------------------------
/scripts/extendCommitHash.ts:
--------------------------------------------------------------------------------
1 | import fs from "node:fs";
2 | import { execSync } from "node:child_process";
3 | import colors from "picocolors";
4 |
5 | export default function extendCommitHash(path: string): void {
6 | let content = fs.readFileSync(path, "utf-8");
7 | const base = "https://github.com/codetie-ai/codetie/commit/";
8 | const matchHashReg = new RegExp(`${base}(\\w{7})\\)`, "g");
9 | console.log(colors.cyan(`\nextending commit hash in ${path}...`));
10 | let match;
11 | while ((match = matchHashReg.exec(content))) {
12 | const shortHash = match[1];
13 | try {
14 | const longHash = execSync(`git rev-parse ${shortHash}`).toString().trim();
15 | content = content.replace(`${base}${shortHash}`, `${base}${longHash}`);
16 | } catch {}
17 | }
18 | fs.writeFileSync(path, content);
19 | console.log(colors.green(`${path} update success!`));
20 | }
21 |
--------------------------------------------------------------------------------
/scripts/release.ts:
--------------------------------------------------------------------------------
1 | import { execSync } from 'child_process';
2 | import fs from 'fs';
3 | import path from 'path';
4 |
5 | const packages = ['codetie', 'create-codetie'];
6 |
7 | function run(command: string, cwd: string) {
8 | console.log(`Executing: ${command} in ${cwd}`);
9 | execSync(command, { stdio: 'inherit', cwd });
10 | }
11 |
12 | async function publishPackages() {
13 | for (const pkg of packages) {
14 | const pkgPath = path.resolve(`packages/${pkg}`);
15 | const pkgJson = JSON.parse(fs.readFileSync(path.join(pkgPath, 'package.json'), 'utf-8'));
16 |
17 | console.log(`Publishing ${pkg}@${pkgJson.version}...`);
18 | run('pnpm publish --no-git-checks', pkgPath);
19 | }
20 | }
21 |
22 | publishPackages().catch(console.error);
--------------------------------------------------------------------------------
/scripts/releaseUtils.ts:
--------------------------------------------------------------------------------
1 | import fs from 'node:fs/promises'
2 | import path from 'node:path'
3 | import colors from 'picocolors'
4 | import type { Options as ExecaOptions, ResultPromise } from 'execa'
5 | import { execa } from 'execa'
6 |
7 | export function run(
8 | bin: string,
9 | args: string[],
10 | opts?: EO,
11 | ): ResultPromise<
12 | EO & (keyof EO extends 'stdio' ? object : { stdio: 'inherit' })
13 | > {
14 | return execa(bin, args, { stdio: 'inherit', ...opts }) as any
15 | }
16 |
17 | export async function getLatestTag(pkgName: string): Promise {
18 | const pkgJson = JSON.parse(
19 | await fs.readFile(`packages/${pkgName}/package.json`, 'utf-8'),
20 | )
21 | const version = pkgJson.version
22 | return pkgName === 'codetie' ? `v${version}` : `${pkgName}@${version}`
23 | }
24 |
25 | export async function logRecentCommits(pkgName: string): Promise {
26 | const tag = await getLatestTag(pkgName)
27 | if (!tag) return
28 | const sha = await run('git', ['rev-list', '-n', '1', tag], {
29 | stdio: 'pipe',
30 | }).then((res) => res.stdout.trim())
31 | console.log(
32 | colors.bold(
33 | `\n${colors.blue(`i`)} Commits of ${colors.green(
34 | pkgName,
35 | )} since ${colors.green(tag)} ${colors.gray(`(${sha.slice(0, 5)})`)}`,
36 | ),
37 | )
38 | await run(
39 | 'git',
40 | [
41 | '--no-pager',
42 | 'log',
43 | `${sha}..HEAD`,
44 | '--oneline',
45 | '--',
46 | `packages/${pkgName}`,
47 | ],
48 | { stdio: 'inherit' },
49 | )
50 | console.log()
51 | }
52 |
53 | export async function updateTemplateVersions(): Promise {
54 | const codetiePkgJson = JSON.parse(
55 | await fs.readFile('packages/codetie/package.json', 'utf-8'),
56 | )
57 | const codetieVersion = codetiePkgJson.version
58 | if (/beta|alpha|rc/.test(codetieVersion)) return
59 |
60 | const dir = 'packages/create-codetie'
61 | const templates = (await fs.readdir(dir)).filter((dir) =>
62 | dir.startsWith('template-'),
63 | )
64 | for (const template of templates) {
65 | const pkgPath = path.join(dir, template, `package.json`)
66 | const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf-8'))
67 | pkg.devDependencies.codetie = `^` + codetieVersion
68 | await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
69 | }
70 | }
--------------------------------------------------------------------------------