@bacons/xcode
Xcode 项目文件的最快和最准确的解析器(.pbxproj) (中文(简体) ). 10-28转快 而不是具有更好的错误信息和完全光谱合规性的替代品(xcode,XcodeProj,xcodeproj).
bun add @bacons/xcode
业绩
以 bun run bench 或者说 bun run bench:compare 为跨语言比较.
| 解析器 | 语言 | 时间( 29KB) | 时间 (263KB) | 相对 |
|---|---|---|---|---|
| @ bacons/ xcode( 代码) | 类型脚本 | 0.15毫米口径 | 0.81亿 | 1个 |
| xcode( 遗产) | Java 脚本 | 1.54毫米口径 | 崩溃 | 10x 慢点 |
| XcodeProj (图解) | 挥发 | 2.00毫米口径 | 11.2ms (中文(简体) ) | 13 个慢点 |
| xcodeproj (可可花) (CocoaPods) (英语) | 鲁比 | 3.63亿 | 22.5毫米口径 | 慢点 24x |
关键性能特征
- 10-28转快 而非替代品(xcode、XcodeProj、xcodeproj)
- 无中间代表制的单行道解析
- 字符分类预计算查询表
- 处理崩溃后遗存解析器的文件
以下是用于解析的语法图:
为什么
最受欢迎的解析 pbxproj 文件的解决方案是由 Cordova 调用的一个非常旧的软件包 x码。 。 。 .
不过 xcode 有一些主要问题:
- 不准确的解析:字符串可以被错误地引用非常多,列表往往不起作用.
- 过期: 缺少 App Clips, iMessage 粘贴包等的值 .
- 未类型: TypeScript 是一个我骄傲地支持的拐杖.
- 慢:PEG.js不快(基准) (中文(简体) ).
- 特性不完整: 缺少
Data类型(<xx xx xx>) (中文(简体) ).
格式比较
考虑下列格式比较.
投入 .pbxproj
307D28A1123043350040C0FA /* app-icon.png */ = {
isa = PBXFileReference;
lastKnownFileType = image.png;
path = "app-icon.png";
sourceTree = "<group>";
};
xcode 产出(旧)
{
"307D28A1123043350040C0FA_comment": "app-icon.png",
"308D052E1370CCF300D202BF": {
"isa": "PBXFileReference",
"lastKnownFileType": "image.png",
"path": "\"app-icon.png\"",
"sourceTree": "\"<group>\""
}
}
同样的物体会看起来像这样 @bacons/xcode数字 :
@bacons/xcode 产出(NEW)
{
"308D052E1370CCF300D202BF": {
"isa": "PBXFileReference",
"lastKnownFileType": "image.png",
"path": "app-icon.png",
"sourceTree": "<group>"
}
}
注意你不需要剥去或重新应用引文,你也不需要过滤出注释,因为默认访问者忽略了评论,倾向于像Xcode那样动态地再生成.
API 密码
有可变相图层,使得与pbxproj工作更容易.
import {
PBXAggregateTarget,
PBXFrameworksBuildPhase,
PBXLegacyTarget,
PBXNativeTarget,
XcodeProject,
} from "@bacons/xcode";
const project = XcodeProject.open("/path/to/project.pbxproj");
// Get all targets:
project.rootObject.props.targets;
创建 Swift 文件 :
import { PBXBuildFile, PBXFileReference } from "@bacons/xcode";
import path from "path";
// Get `project` from XcodeProject.
const file = PBXBuildFile.create(project, {
fileRef: PBXFileReference.create(project, {
path: "MyFile.swift",
sourceTree: "<group>",
}),
});
// The file and fileRef will now be injected in the pbxproj `objects` dict.
XCScheme 支持
解析和操作 Xcode 方案文件( S).xcscheme) (中文(简体) ). 计划定义了目标如何构建、运行、测试、剖析、分析和存档.
低级 API
import * as scheme from "@bacons/xcode/scheme";
import fs from "fs";
// Parse an xcscheme file
const xml = fs.readFileSync(
"/path/to/Project.xcodeproj/xcshareddata/xcschemes/App.xcscheme",
"utf-8",
);
const xcscheme = scheme.parse(xml);
// Modify the scheme
xcscheme.buildAction.parallelizeBuildables = true;
xcscheme.launchAction.environmentVariables = [
{ key: "DEBUG", value: "1", isEnabled: true },
];
// Serialize back to XML
const outputXml = scheme.build(xcscheme);
fs.writeFileSync("/path/to/App.xcscheme", outputXml);
高级API
import { XcodeProject, XCScheme } from "@bacons/xcode";
// Load project and get schemes
const project = XcodeProject.open("/path/to/project.pbxproj");
const schemes = project.getSchemes();
const appScheme = project.getScheme("App");
// Create a new scheme
const newScheme = XCScheme.create("MyNewScheme");
newScheme.addBuildTarget({
buildableIdentifier: "primary",
blueprintIdentifier: "ABC123", // Target UUID
buildableName: "App.app",
blueprintName: "App",
referencedContainer: "container:Project.xcodeproj",
});
newScheme.setLaunchEnvironmentVariable("API_URL", "https://api.example.com");
newScheme.addLaunchArgument("-verbose");
// Save to disk
project.saveScheme(newScheme);
// Create scheme for an existing target
const mainTarget = project.rootObject.props.targets[0];
const targetScheme = project.createSchemeForTarget(mainTarget);
project.saveScheme(targetScheme);
计划管理
分析并构建 xcschememanagement.plist 用于控制方案可见度和命令的文件 :
import * as scheme from "@bacons/xcode/scheme";
import fs from "fs";
const plist = fs.readFileSync("/path/to/xcschememanagement.plist", "utf-8");
const management = scheme.parseManagement(plist);
// Check scheme visibility
console.log(management.SchemeUserState?.["App.xcscheme"]?.isShown);
// Rebuild plist
const output = scheme.buildManagement(management);
XC 工作空间支持
解析和操作 Xcode 工作空间文件( S).xcworkspace) (中文(简体) ). 工作空间将多个Xcode项目组合在一起,通常与CocoaPods,monrepos,以及多项目设置一起使用.
低级 API
import * as workspace from "@bacons/xcode/workspace";
import fs from "fs";
// Parse a workspace file
const xml = fs.readFileSync(
"/path/to/MyApp.xcworkspace/contents.xcworkspacedata",
"utf-8",
);
const ws = workspace.parse(xml);
// Access project references
console.log(ws.fileRefs); // [{ location: "group:App.xcodeproj" }, ...]
// Modify the workspace
ws.fileRefs?.push({ location: "group:NewProject.xcodeproj" });
// Serialize back to XML
const outputXml = workspace.build(ws);
fs.writeFileSync("/path/to/contents.xcworkspacedata", outputXml);
高级API
import { XCWorkspace } from "@bacons/xcode";
// Open an existing workspace
const workspace = XCWorkspace.open("/path/to/MyApp.xcworkspace");
// Get all project paths
const projects = workspace.getProjectPaths();
console.log(projects); // ["group:App.xcodeproj", "group:Pods/Pods.xcodeproj"]
// Check if workspace contains a project
if (!workspace.hasProject("NewProject.xcodeproj")) {
workspace.addProject("NewProject.xcodeproj");
}
// Remove a project
workspace.removeProject("OldProject.xcodeproj");
// Work with groups
const group = workspace.addGroup("Libraries", "group:Libraries");
group.fileRefs?.push({ location: "group:Libraries/MyLib.xcodeproj" });
// Save changes
workspace.save();
// Create a new workspace
const newWorkspace = XCWorkspace.create("MyWorkspace");
newWorkspace.addProject("App.xcodeproj");
newWorkspace.addProject("Pods/Pods.xcodeproj");
newWorkspace.save("/path/to/MyWorkspace.xcworkspace");
位置类型
工作空间文件引用使用位置描述符 :
group:path- 工作空间根的相对路径(最常见的)self:- 自我参考(内嵌工作空间).xcodeproj(中文(简体) )container:path- 绝对集装箱参考(稀有)absolute:path- 绝对文件路径
IDE 工作空间检查
管理 IDEWorkspaceChecks.plist 存储工作空间检查状态的文件。 在Xcode 9.3中被引入,这些文件阻止了Xcode每次打开工作空间时都会重新计算检查.
主要用途是压制 macOS 32-bit 分解警告:
import { XCWorkspace, IDEWorkspaceChecks } from "@bacons/xcode";
// Suppress the 32-bit deprecation warning
const workspace = XCWorkspace.open("/path/to/MyApp.xcworkspace");
workspace.setMac32BitWarningComputed();
// Or work with IDEWorkspaceChecks directly
const checks = IDEWorkspaceChecks.openOrCreate("/path/to/MyApp.xcworkspace");
checks.mac32BitWarningComputed = true;
checks.save();
// Low-level API
import * as workspace from "@bacons/xcode/workspace";
const plist = workspace.parseChecks(plistString);
console.log(plist.IDEDidComputeMac32BitWarning); // true
const output = workspace.buildChecks({ IDEDidComputeMac32BitWarning: true });
XCFig 支持
解析和操作 Xcode 配置文件( S).xcconfig) (中文(简体) ). 这些文件定义了可以在目标和配置之间共享的构建设置.
低级 API
import * as xcconfig from "@bacons/xcode/xcconfig";
import fs from "fs";
// Parse an xcconfig string
const config = xcconfig.parse(`
#include "Base.xcconfig"
PRODUCT_NAME = MyApp
OTHER_LDFLAGS[sdk=iphoneos*] = -framework UIKit
`);
// Parse from file (resolves #include directives)
const config = xcconfig.parseFile("/path/to/Project.xcconfig");
// Flatten build settings (merges includes, applies conditions)
const allSettings = xcconfig.flattenBuildSettings(config);
// Filter by platform conditions
const iosSettings = xcconfig.flattenBuildSettings(config, {
sdk: "iphoneos",
arch: "arm64",
config: "Release",
});
// Serialize back to xcconfig format
const output = xcconfig.build(config);
fs.writeFileSync("/path/to/Project.xcconfig", output);
条件设置
XCCConfig支持基于SDK,架构,和配置的有条件设置 :
// SDK-specific settings
OTHER_LDFLAGS[sdk=iphoneos*] = -framework UIKit
OTHER_LDFLAGS[sdk=macosx*] = -framework AppKit
// Architecture-specific settings
ARCHS[arch=arm64] = arm64
ARCHS[arch=x86_64] = x86_64
// Configuration-specific settings
GCC_OPTIMIZATION_LEVEL[config=Debug] = 0
GCC_OPTIMIZATION_LEVEL[config=Release] = s
// Combined conditions
LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64] = /usr/lib/arm64
包含指令
// Required include - throws if file not found
#include "Base.xcconfig"
// Optional include - silently ignored if file not found
#include? "Optional.xcconfig"
可变扩展
XCCFig 支持变量引用和 $(inherited) 关键字 :
// Reference other settings
PRODUCT_BUNDLE_IDENTIFIER = $(BUNDLE_ID_PREFIX).$(PRODUCT_NAME:lower)
// Inherit from included files
OTHER_LDFLAGS = $(inherited) -framework UIKit
XC 共享数据支持
访问和操纵共享数据目录( U)xcshareddata)包含用于版本控制的系统、断点和工作空间设置.
高级API
import { XcodeProject, XCSharedData } from "@bacons/xcode";
// Get shared data from a project
const project = XcodeProject.open("/path/to/project.pbxproj");
const sharedData = project.getSharedData();
// Access schemes
const schemes = sharedData.getSchemes();
const appScheme = sharedData.getScheme("App");
// Access breakpoints
if (sharedData.breakpoints) {
console.log(sharedData.breakpoints.breakpoints?.length);
}
// Access workspace settings
if (sharedData.workspaceSettings) {
console.log(sharedData.workspaceSettings.PreviewsEnabled);
}
// Modify and save
sharedData.workspaceSettings = {
PreviewsEnabled: true,
IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded: false,
};
sharedData.save();
断点 API
解析和构建 Xcode 断点文件( S)Breakpoints_v2.xcbkptlist:
import * as breakpoints from "@bacons/xcode/breakpoints";
import fs from "fs";
// Parse breakpoint file
const xml = fs.readFileSync(
"/path/to/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist",
"utf-8",
);
const list = breakpoints.parse(xml);
// Access breakpoints
for (const bp of list.breakpoints ?? []) {
console.log(bp.breakpointExtensionID); // "Xcode.Breakpoint.FileBreakpoint"
console.log(bp.breakpointContent?.filePath);
console.log(bp.breakpointContent?.startingLineNumber);
}
// Add a new breakpoint
list.breakpoints?.push({
breakpointExtensionID: "Xcode.Breakpoint.FileBreakpoint",
breakpointContent: {
uuid: "new-uuid",
shouldBeEnabled: true,
filePath: "MyApp/ViewController.swift",
startingLineNumber: "42",
endingLineNumber: "42",
actions: [
{
actionExtensionID: "Xcode.BreakpointAction.DebuggerCommand",
actionContent: { consoleCommand: "po self" },
},
],
},
});
// Serialize back to XML
const outputXml = breakpoints.build(list);
工作空间设置 API
分析并构建工作空间设置文件( E)WorkspaceSettings.xcsettings:
import * as settings from "@bacons/xcode/settings";
import fs from "fs";
// Parse settings file
const plist = fs.readFileSync(
"/path/to/xcshareddata/WorkspaceSettings.xcsettings",
"utf-8",
);
const config = settings.parse(plist);
console.log(config.BuildSystemType); // "Original" or "New"
console.log(config.PreviewsEnabled); // true/false
// Modify and save
config.PreviewsEnabled = true;
config.IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded = false;
const outputPlist = settings.build(config);
fs.writeFileSync("/path/to/WorkspaceSettings.xcsettings", outputPlist);
Swift 软件包管理器支持
在您的 Xcode 工程中添加 Swift 包管理器依赖性, 并自动处理全线布线 .
添加远程软件包
import { XcodeProject } from "@bacons/xcode";
const project = XcodeProject.open("/path/to/project.pbxproj");
const rootProject = project.rootObject;
// Add a remote Swift package to the project
const packageRef = rootProject.addRemoteSwiftPackage({
repositoryURL: "https://github.com/apple/swift-collections",
requirement: {
kind: "upToNextMajorVersion",
minimumVersion: "1.0.0",
},
});
// Add the package product to a target
const target = rootProject.getMainAppTarget("ios");
const productDep = target.addSwiftPackageProduct({
productName: "Collections",
package: packageRef,
});
// Save the project
fs.writeFileSync("/path/to/project.pbxproj", build(project.toJSON()));
添加本地软件包
// Add a local Swift package (e.g., from a monorepo)
const localPackage = rootProject.addLocalSwiftPackage({
relativePath: "../Packages/MyFeature",
});
// Add the product to your target
target.addSwiftPackageProduct({
productName: "MyFeature",
package: localPackage,
});
何为连线
当你打电话 target.addSwiftPackageProduct(),以下自动处理:
- 创建
XCSwiftPackageProductDependency并添加到目标packageProductDependencies - 创建
PBXBuildFile与productRef说明依赖性 - 将构建文件添加到目标框架构建相
管理软件包依赖
// Get all package product dependencies for a target
const deps = target.getSwiftPackageProductDependencies();
// Find an existing package by URL or path
const existing = rootProject.getPackageReference(
"https://github.com/apple/swift-collections"
);
// Remove a package product from a target (cleans up build file too)
target.removeSwiftPackageProduct(productDep);
版本要求
远程包支持各种版本的要求类型 :
// Up to next major version (e.g., 1.0.0 to 2.0.0)
{ kind: "upToNextMajorVersion", minimumVersion: "1.0.0" }
// Up to next minor version (e.g., 1.2.0 to 1.3.0)
{ kind: "upToNextMinorVersion", minimumVersion: "1.2.0" }
// Exact version
{ kind: "exactVersion", version: "1.2.3" }
// Version range
{ kind: "versionRange", minimumVersion: "1.0.0", maximumVersion: "2.0.0" }
// Branch
{ kind: "branch", branch: "main" }
// Revision (commit hash)
{ kind: "revision", revision: "abc123def456" }
解决方案
- 使用比遗产更快的手取最优化的单通解析器
xcode软件包(使用PEG.js). - 此工程支持数据类型
<xx xx xx>。 。 。 . - 更好的带有行和列编号的错误消息 .
- 由于我们遵循《公约》,因此执行工作更加稳定 最佳猜测 pbxproj 光谱。 。 。 .
- 弦分析是最狡猾的部分。 此软件包使用实际的端口 CFoldStylePlist 解析器 该方法是环境署在规模上首先使用的方法 可可扑克队 (原始内容贷记到 萨曼莎·马歇尔) (中文(简体) ).
怎么样
解析非常简单(简单是关键).
pbxproj 是一个"旧式的 plist"(或ASCII Plist),这意味着它应该能够作为JSON或XML等任何其他静态配置文件类型来表示.
我们支持下列类型: Object, (中文(简体) ), Array, (中文(简体) ), Data, (中文(简体) ), String值得注意的是,我们避免处理 Integer, (中文(简体) ), Double,布尔,因为它们在格式上似乎不存在.
开始
- 读取.
- 写作.
- 逃出脚本和页眉搜索路径.
- 生成 UUID .
- 参考型API.
- 构建设置解析 .
- xcscheme 支持 .
- 基准
bun run bench) (中文(简体) ). - x工作空间支持.
- XC 图形 分析 :
.xcconfig文件解析#include支持和构建设置平整. - XC 共享数据: 共享项目数据目录(计划,断点,工作空间设置).
- XC 计划管理: 计划订购、能见度和管理清单.
- 工作空间安排: (中文(简体) )
xcshareddata/WorkspaceSettings.xcsettings)衍生出的数据位置,构建系统版本,自动创建方案设置. - XCBreakpoint 列表: (中文(简体) )
xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist) 共享调试断点(文件,符号,例外断点). - 从零开始创建强大的 xcode 工程 .
- 技术.
- 从其他工具导入.
- XCUserData 数据: (中文(简体) )
xcuserdata/<user>.xcuserdatad/) 用户计划,断点,UI状态. - IDE 工作空间检查: (中文(简体) )
xcshareddata/IDEWorkspaceChecks.plist) 工作空间检查状态存储(例如32位去普率警告). - Swift 软件包管理器: 以自动接线方式添加远程和本地的SPM依赖性.
文档
医生在工作。 现在,你可以提到 类型 和估计数 pbxproj 光谱。 。 。 .
API在未来会有所改变,因为现在我们有两种方法:
import {
/** Given a stringified `pbxproj`, return a JSON representation of the object. */
parse,
/** Given a JSON representation of a `pbxproj`, return a `.pbxproj` string that can be parsed by Xcode. */
build,
} from "@bacons/xcode/json";
import fs from "fs";
import path from "path";
const pbxproj = parse(fs.readFileSync("/path/to/project.pbxproj"));
const pbxprojString = build(pbxproj);
PBXVariantGroup是一个本地化PBXGroup。 。 。 .
文件路径解析
文件将有一个属性 sourceTree 它说明文件路径应如何解决.
BUILT_PRODUCTS_DIR:路径与已建产品目录相对.DEVELOPER_DIR:路径相对开发者目录.SOURCE_ROOT:路径与项目相对.SDKROOT:路径相对SDK目录.<group>:路径与组相对.<absolute>: source是一条绝对路径.
例如,一个文件对象,例如:
{
"isa": "PBXFileReference",
"name": "AppDelegate.m",
"path": "multitarget/AppDelegate.m",
"sourceTree": "<group>"
}
指出 path “多目标/AppDelegate.m”相对 sourceTree ""("".". 我们需要检查包含 PBXGroup是因为 path (仅在组与文件系统中的目录链接时定义). 群体可以生活在其他群体中,因此这一过程是递归性的.
版本
某些值 粗略地映射到对方。 比如顶级 objectVersion (表示用于顶层对象的版本) objects 词典、地图 rootObject - > (中文) PBXProject是因为 compatibilityVersion 绳子。 以下是最新地图(2022年5月):
PBXProject.compatibilityVersion | XcodeProject.objectVersion |
|---|---|
'Xcode 16.0' | 70 |
'Xcode 15.0' | 60 |
'Xcode 14.0' | 56 |
'Xcode 13.0' | 55 |
'Xcode 12.0' | 54 |
'Xcode 11.4' | 53 |
'Xcode 11.0' | 52 |
'Xcode 10.0' | 51 |
'Xcode 9.3' | 50 |
'Xcode 8.0' | 48 |
'Xcode 6.3' | 47 |
'Xcode 3.2' | 46 |
'Xcode 3.1' | 45 |
归属
- 最佳猜测 API doc。 。 。 .
- 可可Pods/Xcodeproj( 可可站/Xcodeproj)。 。 。 .