react-native-bootsplash
Version:
Display a bootsplash on your app starts. Hide it when you want.
274 lines (273 loc) • 11.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.generate = void 0;
var Expo = _interopRequireWildcard(require("@expo/config-plugins"));
var _plist = _interopRequireDefault(require("@expo/plist"));
var _fastGlob = _interopRequireDefault(require("fast-glob"));
var _fs = _interopRequireDefault(require("fs"));
var _nodeHtmlParser = require("node-html-parser");
var _path = _interopRequireDefault(require("path"));
var _picocolors = _interopRequireDefault(require("picocolors"));
var _tsDedent = require("ts-dedent");
var _xmlFormatter = _interopRequireDefault(require("xml-formatter"));
var _utils = require("./utils.js");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
const cwd = process.env.INIT_CWD ?? process.env.PWD ?? process.cwd();
(0, _utils.setLoggerMode)({
type: "cli",
cwd
});
const projectRoot = (() => {
let directory = _path.default.resolve(cwd);
const {
root
} = _path.default.parse(directory);
const ends = new Set([root, _path.default.resolve(directory, root)]);
while (directory) {
const pkgPath = _path.default.join(directory, "package.json");
const stats = _fs.default.statSync(pkgPath, {
throwIfNoEntry: false
});
if (stats != null && stats.isFile()) {
return _path.default.dirname(pkgPath);
}
if (ends.has(directory)) {
break;
} else {
directory = _path.default.dirname(directory);
}
}
})();
if (!projectRoot) {
_utils.log.error("We couldn't find a package.json in your project.");
process.exit(1);
}
const getAndroidOutputPath = ({
flavor
}) => {
const sourceDir = _path.default.join(projectRoot, "android");
if (!_fs.default.existsSync(sourceDir)) {
return;
}
const appDir = _path.default.join(sourceDir, "app");
const androidOutputPath = _path.default.resolve(sourceDir, _fs.default.existsSync(appDir) ? "app" : "", "src", flavor, "res");
if (_utils.hfs.exists(androidOutputPath)) {
return androidOutputPath;
}
_utils.log.warn(`No ${_path.default.relative(cwd, androidOutputPath)} directory found. Skipping Android assets generation…`);
};
const getIOSOutputPath = () => {
const podfile = _fastGlob.default.sync("**/Podfile", {
cwd: projectRoot.replace(/^([a-zA-Z]+:|\.\/)/, ""),
deep: 10,
ignore: ["**/@(Pods|node_modules|Carthage|vendor|android)/**"]
}).find(project => {
return _path.default.dirname(project) === "ios";
});
if (!podfile) {
return;
}
const sourceDir = _path.default.dirname(_path.default.join(projectRoot, podfile));
const xcodeProjectName = _fs.default.readdirSync(sourceDir).sort().reverse().find(fileName => {
const ext = _path.default.extname(fileName);
return ext === ".xcodeproj" || ext === ".xcworkspace";
});
if (xcodeProjectName == null) {
_utils.log.warn("No Xcode project found. Skipping iOS assets generation…");
return;
}
const iosOutputPath = _path.default.resolve(sourceDir, xcodeProjectName).replace(/\.(xcodeproj|xcworkspace)$/, "");
if (_utils.hfs.exists(iosOutputPath)) {
return iosOutputPath;
}
_utils.log.warn(`No ${_path.default.relative(cwd, iosOutputPath)} directory found. Skipping iOS assets generation…`);
};
const getHtmlTemplatePath = ({
html
}) => {
const htmlTemplatePath = _path.default.resolve(cwd, html);
if (_utils.hfs.exists(htmlTemplatePath)) {
return htmlTemplatePath;
}
_utils.log.warn(`No ${_path.default.relative(cwd, htmlTemplatePath)} found. Skipping HTML + CSS generation…`);
};
const getInfoPlistPath = ({
iosOutputPath,
plist
}) => {
if (plist != null) {
const infoPlistPath = _path.default.resolve(cwd, plist);
if (!_utils.hfs.exists(infoPlistPath)) {
return _utils.log.warn(`No ${_path.default.relative(cwd, infoPlistPath)} found`);
}
return infoPlistPath;
}
return _path.default.resolve(iosOutputPath, "Info.plist");
};
const generate = async ({
platforms,
html,
flavor,
plist,
...rawProps
}) => {
const props = await (0, _utils.transformProps)(cwd, rawProps);
const addon = (0, _utils.requireAddon)(props);
const {
background,
brand
} = props;
const androidOutputPath = platforms.includes("android") ? getAndroidOutputPath({
flavor
}) : undefined;
const iosOutputPath = platforms.includes("ios") ? getIOSOutputPath() : undefined;
const htmlTemplatePath = platforms.includes("web") ? getHtmlTemplatePath({
html
}) : undefined;
if (androidOutputPath != null) {
await (0, _utils.writeAndroidAssets)({
androidOutputPath,
props
});
const manifestXmlPath = _path.default.resolve(androidOutputPath, "..", "AndroidManifest.xml");
if (_utils.hfs.exists(manifestXmlPath)) {
const manifestXml = (0, _utils.readXmlLike)(manifestXmlPath);
const activities = manifestXml.root.querySelectorAll("activity");
for (const activity of activities) {
if (activity.getAttribute("android:name") === ".MainActivity") {
activity.setAttribute("android:theme", "@style/BootTheme");
}
}
await (0, _utils.writeXmlLike)(manifestXmlPath, manifestXml.root.toString(), {
...manifestXml.formatOptions,
formatter: "prettier",
htmlWhitespaceSensitivity: "ignore",
selfClosingTags: true,
singleAttributePerLine: true
});
} else {
_utils.log.warn("No AndroidManifest.xml found");
}
const valuesPath = _path.default.resolve(androidOutputPath, "values");
_utils.hfs.ensureDir(valuesPath);
const colorsXmlPath = _path.default.resolve(valuesPath, "colors.xml");
const colorsXmlEntry = `<color name="bootsplash_background">${background.hex}</color>`;
if (_utils.hfs.exists(colorsXmlPath)) {
const colorsXml = (0, _utils.readXmlLike)(colorsXmlPath);
const nextColor = (0, _nodeHtmlParser.parse)(colorsXmlEntry);
const prevColor = colorsXml.root.querySelector('color[name="bootsplash_background"]');
if (prevColor != null) {
prevColor.replaceWith(nextColor);
} else {
colorsXml.root.querySelector("resources")?.appendChild(nextColor);
}
await (0, _utils.writeXmlLike)(colorsXmlPath, colorsXml.root.toString(), {
...colorsXml.formatOptions,
formatter: "xmlFormatter"
});
} else {
await (0, _utils.writeXmlLike)(colorsXmlPath, `<resources>${colorsXmlEntry}</resources>`, {
formatter: "xmlFormatter"
});
}
const stylesXmlPath = _path.default.resolve(valuesPath, "styles.xml");
if (_utils.hfs.exists(stylesXmlPath)) {
const stylesXml = (0, _utils.readXmlLike)(stylesXmlPath);
const prevStyle = stylesXml.root.querySelector('style[name="BootTheme"]');
const parent = prevStyle?.getAttribute("parent") ?? "Theme.BootSplash";
const extraItems = (0, _nodeHtmlParser.parse)(prevStyle?.text.split("\n").map(line => line.trim()).join("") ?? "").childNodes.filter(node => {
if (!(node instanceof _nodeHtmlParser.HTMLElement)) {
return true;
}
const name = node.getAttribute("name");
return name !== "bootSplashBackground" && name !== "bootSplashLogo" && name !== "bootSplashBrand" && name !== "postBootSplashTheme";
}).map(node => node.toString());
const styleItems = [...(extraItems.length > 0 ? [...extraItems, ""] : []), '<item name="bootSplashBackground">@color/bootsplash_background</item>', '<item name="bootSplashLogo">@drawable/bootsplash_logo</item>', ...(brand != null ? ['<item name="bootSplashBrand">@drawable/bootsplash_brand</item>'] : []), '<item name="postBootSplashTheme">@style/AppTheme</item>'];
const nextStyle = (0, _nodeHtmlParser.parse)((0, _tsDedent.dedent)`
<style name="BootTheme" parent="${parent}">
${styleItems.join("\n")}
</style>
`);
prevStyle?.remove(); // remove the existing style
stylesXml.root.querySelector("resources")?.appendChild(nextStyle);
await (0, _utils.writeXmlLike)(stylesXmlPath, stylesXml.root.toString(), {
...stylesXml.formatOptions,
formatter: "prettier",
htmlWhitespaceSensitivity: "ignore"
});
} else {
_utils.log.warn("No styles.xml found");
}
}
if (iosOutputPath != null) {
await (0, _utils.writeIOSAssets)({
iosOutputPath,
props
});
const pbxprojectPath = Expo.IOSConfig.Paths.getPBXProjectPath(projectRoot);
const xcodeProjectPath = Expo.IOSConfig.Paths.getXcodeProjectPath(projectRoot);
const project = Expo.IOSConfig.XcodeUtils.getPbxproj(projectRoot);
const projectName = _path.default.basename(iosOutputPath);
const groupName = _path.default.parse(xcodeProjectPath).name;
Expo.IOSConfig.XcodeUtils.addResourceFileToGroup({
project,
filepath: _path.default.join(projectName, "BootSplash.storyboard"),
groupName,
isBuildFile: true
});
Expo.IOSConfig.XcodeUtils.addResourceFileToGroup({
project,
filepath: _path.default.join(projectName, "Colors.xcassets"),
groupName,
isBuildFile: true
});
_utils.hfs.write(pbxprojectPath, project.writeSync());
_utils.log.write(pbxprojectPath);
const infoPlistPath = getInfoPlistPath({
iosOutputPath,
plist
});
if (infoPlistPath != null) {
const infoPlist = _plist.default.parse(_utils.hfs.text(infoPlistPath));
infoPlist["UILaunchStoryboardName"] = "BootSplash";
const formatted = (0, _xmlFormatter.default)(_plist.default.build(infoPlist), {
collapseContent: true,
forceSelfClosingEmptyTag: false,
indentation: "\t",
lineSeparator: "\n",
whiteSpaceAtEndOfSelfclosingTag: false
}).replace(/<string\/>/gm, "<string></string>").replace(/^\t/gm, "");
_utils.hfs.write(infoPlistPath, formatted);
_utils.log.write(infoPlistPath);
}
}
if (htmlTemplatePath != null) {
await (0, _utils.writeWebAssets)({
htmlTemplatePath,
props
});
}
await (0, _utils.writeGenericAssets)({
props
});
if (addon != null) {
await addon.execute({
props,
androidOutputPath,
iosOutputPath,
htmlTemplatePath
});
} else {
console.log(`
${_picocolors.default.blue("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓")}
${_picocolors.default.blue("┃")} 🔑 ${_picocolors.default.bold("Get a license key for brand image / dark mode support")} ${_picocolors.default.blue("┃")}
${_picocolors.default.blue("┃")} ${_picocolors.default.underline("https://zoontek.gumroad.com/l/bootsplash-generator")} ${_picocolors.default.blue("┃")}
${_picocolors.default.blue("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛")}`);
}
console.log(`\n💖 Thanks for using ${_picocolors.default.underline("react-native-bootsplash")}`);
};
exports.generate = generate;
//# sourceMappingURL=generate.js.map