@bacons/apple-targets 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +218 -0
- package/app.plugin.js +1 -0
- package/build/colorset/customColorFromCSS.d.ts +10 -0
- package/build/colorset/customColorFromCSS.js +23 -0
- package/build/colorset/withIosColorset.d.ts +11 -0
- package/build/colorset/withIosColorset.js +65 -0
- package/build/config.d.ts +103 -0
- package/build/config.js +2 -0
- package/build/icon/withImageAsset.d.ts +26 -0
- package/build/icon/withImageAsset.js +250 -0
- package/build/icon/withIosIcon.d.ts +19 -0
- package/build/icon/withIosIcon.js +211 -0
- package/build/index.d.ts +8 -0
- package/build/index.js +29 -0
- package/build/target.d.ts +10 -0
- package/build/target.js +322 -0
- package/build/template/XCBuildConfiguration.json +759 -0
- package/build/withEasCredentials.d.ts +17 -0
- package/build/withEasCredentials.js +95 -0
- package/build/withWidget.d.ts +7 -0
- package/build/withWidget.js +158 -0
- package/build/withXcodeChanges.d.ts +17 -0
- package/build/withXcodeChanges.js +975 -0
- package/build/withXcparse.d.ts +4 -0
- package/build/withXcparse.js +77 -0
- package/package.json +41 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { XcodeProject } from "@bacons/xcode";
|
|
2
|
+
import { ConfigPlugin } from "expo/config-plugins";
|
|
3
|
+
import { Entitlements } from "./config";
|
|
4
|
+
export declare const withEASTargets: ConfigPlugin<{
|
|
5
|
+
bundleIdentifier: string;
|
|
6
|
+
targetName: string;
|
|
7
|
+
entitlements?: Record<string, any>;
|
|
8
|
+
}>;
|
|
9
|
+
type EASCredentials = {
|
|
10
|
+
targetName: string;
|
|
11
|
+
bundleIdentifier: string;
|
|
12
|
+
parentBundleIdentifier: string;
|
|
13
|
+
entitlements?: Entitlements;
|
|
14
|
+
};
|
|
15
|
+
export declare const withAutoEasExtensionCredentials: ConfigPlugin;
|
|
16
|
+
export declare function getEASCredentialsForXcodeProject(project: XcodeProject): EASCredentials[];
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getEASCredentialsForXcodeProject = exports.withAutoEasExtensionCredentials = exports.withEASTargets = void 0;
|
|
4
|
+
const target_1 = require("./target");
|
|
5
|
+
const withXcparse_1 = require("./withXcparse");
|
|
6
|
+
function safeSet(obj, key, value) {
|
|
7
|
+
const segments = key.split(".");
|
|
8
|
+
const last = segments.pop();
|
|
9
|
+
segments.forEach((segment) => {
|
|
10
|
+
if (!obj[segment]) {
|
|
11
|
+
obj[segment] = {};
|
|
12
|
+
}
|
|
13
|
+
obj = obj[segment];
|
|
14
|
+
});
|
|
15
|
+
obj[last] = value;
|
|
16
|
+
return obj;
|
|
17
|
+
}
|
|
18
|
+
const withEASTargets = (config, { bundleIdentifier, targetName, entitlements }) => {
|
|
19
|
+
// Extra EAS targets
|
|
20
|
+
safeSet(config, "extra.eas.build.experimental.ios.appExtensions", []);
|
|
21
|
+
const existing = config.extra.eas.build.experimental.ios.appExtensions.findIndex((ext) => ext.targetName === targetName);
|
|
22
|
+
if (existing > -1) {
|
|
23
|
+
config.extra.eas.build.experimental.ios.appExtensions[existing] = {
|
|
24
|
+
...config.extra.eas.build.experimental.ios.appExtensions[existing],
|
|
25
|
+
bundleIdentifier,
|
|
26
|
+
entitlements: {
|
|
27
|
+
...config.extra.eas.build.experimental.ios.appExtensions[existing]
|
|
28
|
+
.entitlements,
|
|
29
|
+
...entitlements,
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
config.extra.eas.build.experimental.ios.appExtensions.push({
|
|
35
|
+
bundleIdentifier,
|
|
36
|
+
targetName,
|
|
37
|
+
entitlements,
|
|
38
|
+
});
|
|
39
|
+
// "appExtensions": [
|
|
40
|
+
// {
|
|
41
|
+
// "targetName": "widgets",
|
|
42
|
+
// "bundleIdentifier": "com.evanbacon.pillarvalley.widgets",
|
|
43
|
+
// "entitlements": {
|
|
44
|
+
// "com.apple.security.application-groups": [
|
|
45
|
+
// "group.bacon.data"
|
|
46
|
+
// ]
|
|
47
|
+
// }
|
|
48
|
+
// }
|
|
49
|
+
// ]
|
|
50
|
+
}
|
|
51
|
+
return config;
|
|
52
|
+
};
|
|
53
|
+
exports.withEASTargets = withEASTargets;
|
|
54
|
+
const withAutoEasExtensionCredentials = (config) => {
|
|
55
|
+
return (0, withXcparse_1.withXcodeProjectBeta)(config, async (config) => {
|
|
56
|
+
safeSet(config, "extra.eas.build.experimental.ios.appExtensions", []);
|
|
57
|
+
const creds = getEASCredentialsForXcodeProject(config.modResults);
|
|
58
|
+
// Warn about duplicates
|
|
59
|
+
config.extra.eas.build.experimental.ios.appExtensions.forEach((ext) => {
|
|
60
|
+
const existing = creds.find((cred) => cred.bundleIdentifier === ext.bundleIdentifier);
|
|
61
|
+
if (existing &&
|
|
62
|
+
(existing.targetName !== ext.targetName ||
|
|
63
|
+
existing.parentBundleIdentifier !== ext.parentBundleIdentifier)) {
|
|
64
|
+
throw new Error(`EAS credentials already has a target "${ext.targetName}" with bundle identifier: ${ext.bundleIdentifier}.`);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
config.extra.eas.build.experimental.ios.appExtensions = [
|
|
68
|
+
...config.extra.eas.build.experimental.ios.appExtensions,
|
|
69
|
+
...creds,
|
|
70
|
+
];
|
|
71
|
+
return config;
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
exports.withAutoEasExtensionCredentials = withAutoEasExtensionCredentials;
|
|
75
|
+
function getEASCredentialsForXcodeProject(project) {
|
|
76
|
+
const parentBundleIdentifier = (0, target_1.getMainAppTarget)(project).getDefaultConfiguration().props.buildSettings
|
|
77
|
+
.PRODUCT_BUNDLE_IDENTIFIER;
|
|
78
|
+
const targets = (0, target_1.getAuxiliaryTargets)(project);
|
|
79
|
+
return targets.map((target) => {
|
|
80
|
+
var _a;
|
|
81
|
+
const config = target.getDefaultConfiguration();
|
|
82
|
+
const entitlements = config.getEntitlements();
|
|
83
|
+
const targetName = target.props.productName;
|
|
84
|
+
if (!targetName) {
|
|
85
|
+
throw new Error(`Target ${target.getDisplayName()} (${target.uuid}) does not have a productName.`);
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
targetName,
|
|
89
|
+
bundleIdentifier: (_a = config.props.buildSettings) === null || _a === void 0 ? void 0 : _a.PRODUCT_BUNDLE_IDENTIFIER,
|
|
90
|
+
parentBundleIdentifier,
|
|
91
|
+
entitlements,
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
exports.getEASCredentialsForXcodeProject = getEASCredentialsForXcodeProject;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const config_plugins_1 = require("@expo/config-plugins");
|
|
7
|
+
const plist_1 = __importDefault(require("@expo/plist"));
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const glob_1 = require("glob");
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const withIosColorset_1 = require("./colorset/withIosColorset");
|
|
12
|
+
const withImageAsset_1 = require("./icon/withImageAsset");
|
|
13
|
+
const withIosIcon_1 = require("./icon/withIosIcon");
|
|
14
|
+
const target_1 = require("./target");
|
|
15
|
+
const withEasCredentials_1 = require("./withEasCredentials");
|
|
16
|
+
const withXcodeChanges_1 = require("./withXcodeChanges");
|
|
17
|
+
function kebabToCamelCase(str) {
|
|
18
|
+
return str.replace(/-([a-z])/g, function (g) {
|
|
19
|
+
return g[1].toUpperCase();
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
const withWidget = (config, props) => {
|
|
23
|
+
// TODO: Magically based on the top-level folders in the `ios-widgets/` folder
|
|
24
|
+
var _a, _b, _c, _d, _e, _f;
|
|
25
|
+
if (props.icon && !/https?:\/\//.test(props.icon)) {
|
|
26
|
+
props.icon = path_1.default.join(props.directory, props.icon);
|
|
27
|
+
}
|
|
28
|
+
const widgetDir = path_1.default
|
|
29
|
+
.basename(props.directory)
|
|
30
|
+
.replace(/\/+$/, "")
|
|
31
|
+
.replace(/^\/+/, "");
|
|
32
|
+
const widget = kebabToCamelCase(widgetDir);
|
|
33
|
+
const widgetFolderAbsolutePath = path_1.default.join((_b = (_a = config._internal) === null || _a === void 0 ? void 0 : _a.projectRoot) !== null && _b !== void 0 ? _b : "", props.directory);
|
|
34
|
+
const entitlementsFiles = (0, glob_1.sync)("*.entitlements", {
|
|
35
|
+
absolute: true,
|
|
36
|
+
cwd: widgetFolderAbsolutePath,
|
|
37
|
+
});
|
|
38
|
+
if (entitlementsFiles.length > 1) {
|
|
39
|
+
throw new Error(`Found multiple entitlements files in ${widgetFolderAbsolutePath}`);
|
|
40
|
+
}
|
|
41
|
+
let entitlementsJson = props.entitlements;
|
|
42
|
+
// If the user defined entitlements, then overwrite any existing entitlements file
|
|
43
|
+
if (props.entitlements) {
|
|
44
|
+
(0, config_plugins_1.withDangerousMod)(config, [
|
|
45
|
+
"ios",
|
|
46
|
+
async (config) => {
|
|
47
|
+
var _a;
|
|
48
|
+
const entitlementsFilePath = (_a = entitlementsFiles[0]) !== null && _a !== void 0 ? _a :
|
|
49
|
+
// Use the name `generated` to help indicate that this file should be in sync with the config
|
|
50
|
+
path_1.default.join(widgetFolderAbsolutePath, `generated.entitlements`);
|
|
51
|
+
if (entitlementsFiles[0]) {
|
|
52
|
+
console.log(`[${widget}] Replacing ${path_1.default.relative(widgetFolderAbsolutePath, entitlementsFiles[0])} with entitlements JSON from config`);
|
|
53
|
+
}
|
|
54
|
+
fs_1.default.writeFileSync(entitlementsFilePath, plist_1.default.build(props.entitlements));
|
|
55
|
+
return config;
|
|
56
|
+
},
|
|
57
|
+
]);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
entitlementsJson = entitlementsFiles[0]
|
|
61
|
+
? plist_1.default.parse(fs_1.default.readFileSync(entitlementsFiles[0], "utf8"))
|
|
62
|
+
: undefined;
|
|
63
|
+
}
|
|
64
|
+
// Ensure the entry file exists
|
|
65
|
+
(0, config_plugins_1.withDangerousMod)(config, [
|
|
66
|
+
"ios",
|
|
67
|
+
async (config) => {
|
|
68
|
+
fs_1.default.mkdirSync(widgetFolderAbsolutePath, { recursive: true });
|
|
69
|
+
const files = [
|
|
70
|
+
["Info.plist", (0, target_1.getTargetInfoPlistForType)(props.type)],
|
|
71
|
+
];
|
|
72
|
+
// if (props.type === "widget") {
|
|
73
|
+
// files.push(
|
|
74
|
+
// [
|
|
75
|
+
// "index.swift",
|
|
76
|
+
// ENTRY_FILE.replace(
|
|
77
|
+
// "// Export widgets here",
|
|
78
|
+
// "// Export widgets here\n" + ` ${widget}()`
|
|
79
|
+
// ),
|
|
80
|
+
// ],
|
|
81
|
+
// [widget + ".swift", WIDGET.replace(/alpha/g, widget)],
|
|
82
|
+
// [widget + ".intentdefinition", INTENT_DEFINITION]
|
|
83
|
+
// );
|
|
84
|
+
// }
|
|
85
|
+
files.forEach(([filename, content]) => {
|
|
86
|
+
const filePath = path_1.default.join(widgetFolderAbsolutePath, filename);
|
|
87
|
+
if (!fs_1.default.existsSync(filePath)) {
|
|
88
|
+
fs_1.default.writeFileSync(filePath, content);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
return config;
|
|
92
|
+
},
|
|
93
|
+
]);
|
|
94
|
+
const targetName = (_c = props.name) !== null && _c !== void 0 ? _c : widget;
|
|
95
|
+
const bundleId = config.ios.bundleIdentifier + "." + widget;
|
|
96
|
+
(0, withXcodeChanges_1.withXcodeChanges)(config, {
|
|
97
|
+
name: targetName,
|
|
98
|
+
cwd: "../" +
|
|
99
|
+
path_1.default.relative(config._internal.projectRoot, path_1.default.resolve(props.directory)),
|
|
100
|
+
deploymentTarget: (_d = props.deploymentTarget) !== null && _d !== void 0 ? _d : "16.4",
|
|
101
|
+
bundleId,
|
|
102
|
+
icon: props.icon,
|
|
103
|
+
hasAccentColor: !!((_e = props.colors) === null || _e === void 0 ? void 0 : _e.$accent),
|
|
104
|
+
// @ts-expect-error: who cares
|
|
105
|
+
currentProjectVersion: ((_f = config.ios) === null || _f === void 0 ? void 0 : _f.buildNumber) || 1,
|
|
106
|
+
frameworks: (0, target_1.getFrameworksForType)(props.type).concat(props.frameworks || []),
|
|
107
|
+
type: props.type,
|
|
108
|
+
teamId: props.appleTeamId,
|
|
109
|
+
});
|
|
110
|
+
config = (0, withEasCredentials_1.withEASTargets)(config, {
|
|
111
|
+
targetName,
|
|
112
|
+
bundleIdentifier: bundleId,
|
|
113
|
+
entitlements: entitlementsJson,
|
|
114
|
+
});
|
|
115
|
+
if (props.images) {
|
|
116
|
+
Object.entries(props.images).forEach(([name, image]) => {
|
|
117
|
+
(0, withImageAsset_1.withImageAsset)(config, {
|
|
118
|
+
image,
|
|
119
|
+
name,
|
|
120
|
+
cwd: props.directory,
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
withConfigColors(config, props);
|
|
125
|
+
if (props.icon) {
|
|
126
|
+
(0, withIosIcon_1.withIosIcon)(config, {
|
|
127
|
+
type: props.type,
|
|
128
|
+
cwd: props.directory,
|
|
129
|
+
// TODO: read from the top-level icon.png file in the folder -- ERR this doesn't allow for URLs
|
|
130
|
+
iconFilePath: props.icon,
|
|
131
|
+
isTransparent: ["action"].includes(props.type),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return config;
|
|
135
|
+
};
|
|
136
|
+
const withConfigColors = (config, props) => {
|
|
137
|
+
var _a;
|
|
138
|
+
props.colors = (_a = props.colors) !== null && _a !== void 0 ? _a : {};
|
|
139
|
+
// const colors: NonNullable<Props["colors"]> = props.colors ?? {};
|
|
140
|
+
// You use the WidgetBackground and `$accent` to style the widget configuration interface of a configurable widget. Apple could have chosen names to make that more obvious.
|
|
141
|
+
// https://useyourloaf.com/blog/widget-background-and-accent-color/
|
|
142
|
+
// i.e. when you press and hold on a widget to configure it, the background color of the widget configuration interface changes to the background color we set here.
|
|
143
|
+
// if (props.widgetBackgroundColor)
|
|
144
|
+
// colors["$widgetBackground"] = props.widgetBackgroundColor;
|
|
145
|
+
// if (props.accentColor) colors["AccentColor"] = props.accentColor;
|
|
146
|
+
if (props.colors) {
|
|
147
|
+
Object.entries(props.colors).forEach(([name, color]) => {
|
|
148
|
+
(0, withIosColorset_1.withIosColorset)(config, {
|
|
149
|
+
cwd: props.directory,
|
|
150
|
+
name,
|
|
151
|
+
color: typeof color === "string" ? color : color.light,
|
|
152
|
+
darkColor: typeof color === "string" ? undefined : color.dark,
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return config;
|
|
157
|
+
};
|
|
158
|
+
exports.default = withWidget;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ConfigPlugin } from "@expo/config-plugins";
|
|
2
|
+
import { ExtensionType } from "./target";
|
|
3
|
+
export type XcodeSettings = {
|
|
4
|
+
name: string;
|
|
5
|
+
/** Directory relative to the project root, (i.e. outside of the `ios` directory) where the widget code should live. */
|
|
6
|
+
cwd: string;
|
|
7
|
+
bundleId: string;
|
|
8
|
+
deploymentTarget: string;
|
|
9
|
+
currentProjectVersion: number;
|
|
10
|
+
frameworks: string[];
|
|
11
|
+
type: ExtensionType;
|
|
12
|
+
hasAccentColor?: boolean;
|
|
13
|
+
colors?: Record<string, string>;
|
|
14
|
+
teamId?: string;
|
|
15
|
+
icon?: string;
|
|
16
|
+
};
|
|
17
|
+
export declare const withXcodeChanges: ConfigPlugin<XcodeSettings>;
|