@expo/cli
Version:
284 lines (283 loc) • 11.4 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
updatePackageJSONAsync: ()=>updatePackageJSONAsync,
updatePkgDependencies: ()=>updatePkgDependencies,
createDependenciesMap: ()=>createDependenciesMap,
hashForDependencyMap: ()=>hashForDependencyMap,
createFileHash: ()=>createFileHash
});
function _config() {
const data = require("@expo/config");
_config = function() {
return data;
};
return data;
}
function _chalk() {
const data = /*#__PURE__*/ _interopRequireDefault(require("chalk"));
_chalk = function() {
return data;
};
return data;
}
function _crypto() {
const data = /*#__PURE__*/ _interopRequireDefault(require("crypto"));
_crypto = function() {
return data;
};
return data;
}
function _fs() {
const data = /*#__PURE__*/ _interopRequireDefault(require("fs"));
_fs = function() {
return data;
};
return data;
}
function _path() {
const data = /*#__PURE__*/ _interopRequireDefault(require("path"));
_path = function() {
return data;
};
return data;
}
function _semver() {
const data = require("semver");
_semver = function() {
return data;
};
return data;
}
const _log = /*#__PURE__*/ _interopRequireWildcard(require("../log"));
const _isModuleSymlinked = require("../utils/isModuleSymlinked");
const _ora = require("../utils/ora");
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function _getRequireWildcardCache(nodeInterop) {
if (typeof WeakMap !== "function") return null;
var cacheBabelInterop = new WeakMap();
var cacheNodeInterop = new WeakMap();
return (_getRequireWildcardCache = function(nodeInterop) {
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
})(nodeInterop);
}
function _interopRequireWildcard(obj, nodeInterop) {
if (!nodeInterop && obj && obj.__esModule) {
return obj;
}
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
return {
default: obj
};
}
var cache = _getRequireWildcardCache(nodeInterop);
if (cache && cache.has(obj)) {
return cache.get(obj);
}
var newObj = {};
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
for(var key in obj){
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
if (desc && (desc.get || desc.set)) {
Object.defineProperty(newObj, key, desc);
} else {
newObj[key] = obj[key];
}
}
}
newObj.default = obj;
if (cache) {
cache.set(obj, newObj);
}
return newObj;
}
async function updatePackageJSONAsync(projectRoot, { templateDirectory , templatePkg =(0, _config().getPackageJson)(templateDirectory) , pkg , skipDependencyUpdate }) {
const updatingPackageJsonStep = (0, _ora.logNewSection)("Updating package.json");
const results = modifyPackageJson(projectRoot, {
templatePkg,
pkg,
skipDependencyUpdate
});
const hasChanges = results.changedDependencies.length || results.scriptsChanged;
// NOTE: This is effectively bundler caching and subject to breakage if the inputs don't match the mutations.
if (hasChanges) {
await _fs().default.promises.writeFile(_path().default.resolve(projectRoot, "package.json"), // Add new line to match the format of running yarn.
// This prevents the `package.json` from changing when running `prebuild --no-install` multiple times.
JSON.stringify(pkg, null, 2) + "\n");
}
updatingPackageJsonStep.succeed("Updated package.json" + (hasChanges ? "" : _chalk().default.dim(` | no changes`)));
return results;
}
/**
* Make required modifications to the `package.json` file as a JSON object.
*
* 1. Update `package.json` `scripts`.
* 2. Update `package.json` `dependencies` (not `devDependencies`).
* 3. Update `package.json` `main`.
*
* @param projectRoot The root directory of the project.
* @param props.templatePkg Template project package.json as JSON.
* @param props.pkg Current package.json as JSON.
* @param props.skipDependencyUpdate Array of dependencies to skip updating.
* @returns
*/ function modifyPackageJson(projectRoot, { templatePkg , pkg , skipDependencyUpdate }) {
const scriptsChanged = updatePkgScripts({
pkg
});
// TODO: Move to `npx expo-doctor`
return {
scriptsChanged,
...updatePkgDependencies(projectRoot, {
pkg,
templatePkg,
skipDependencyUpdate
})
};
}
function updatePkgDependencies(projectRoot, { pkg , templatePkg , skipDependencyUpdate =[] }) {
const { dependencies } = templatePkg;
// The default values come from the bare-minimum template's package.json.
// Users can change this by using different templates with the `--template` flag.
// The main reason for allowing the changing of dependencies would be to include
// dependencies that are required for the native project to build. For example,
// it does not need to include dependencies that are used in the JS-code only.
const defaultDependencies = createDependenciesMap(dependencies);
// NOTE: This is a hack to ensure this doesn't trigger an extraneous change in the `package.json`
// it isn't required for anything in the `ios` and `android` folders.
delete defaultDependencies["expo-status-bar"];
// NOTE: Expo splash screen is installed by default in the template but the config plugin also lives in prebuild-config
// so we can delete it to prevent an extraneous change in the `package.json`.
delete defaultDependencies["expo-splash-screen"];
const combinedDependencies = createDependenciesMap({
...defaultDependencies,
...pkg.dependencies
});
// These dependencies are only added, not overwritten from the project
const requiredDependencies = [
// TODO: This is no longer required because it's this same package.
"expo",
// TODO: Drop this somehow.
"react-native",
].filter((depKey)=>!!defaultDependencies[depKey]);
const symlinkedPackages = [];
const nonRecommendedPackages = [];
for (const dependenciesKey of requiredDependencies){
var ref;
// If the local package.json defined the dependency that we want to overwrite...
if ((ref = pkg.dependencies) == null ? void 0 : ref[dependenciesKey]) {
// Then ensure it isn't symlinked (i.e. the user has a custom version in their yarn workspace).
if ((0, _isModuleSymlinked.isModuleSymlinked)(projectRoot, {
moduleId: dependenciesKey,
isSilent: true
})) {
// If the package is in the project's package.json and it's symlinked, then skip overwriting it.
symlinkedPackages.push(dependenciesKey);
continue;
}
// Do not modify manually skipped dependencies
if (skipDependencyUpdate.includes(dependenciesKey)) {
continue;
}
// Warn users for outdated dependencies when prebuilding
const hasRecommendedVersion = versionRangesIntersect(pkg.dependencies[dependenciesKey], String(defaultDependencies[dependenciesKey]));
if (!hasRecommendedVersion) {
nonRecommendedPackages.push(`${dependenciesKey}@${defaultDependencies[dependenciesKey]}`);
}
}
}
if (symlinkedPackages.length) {
_log.log(`\u203A Using symlinked ${symlinkedPackages.map((pkg)=>_chalk().default.bold(pkg)).join(", ")} instead of recommended version(s).`);
}
if (nonRecommendedPackages.length) {
_log.warn(`\u203A Using current versions instead of recommended ${nonRecommendedPackages.map((pkg)=>_chalk().default.bold(pkg)).join(", ")}.`);
}
// Only change the dependencies if the normalized hash changes, this helps to reduce meaningless changes.
const hasNewDependencies = hashForDependencyMap(pkg.dependencies) !== hashForDependencyMap(combinedDependencies);
// Save the dependencies
let changedDependencies = [];
if (hasNewDependencies) {
var _dependencies;
changedDependencies = diffKeys(combinedDependencies, (_dependencies = pkg.dependencies) != null ? _dependencies : {}).sort();
var _dependencies1;
// Use Object.assign to preserve the original order of dependencies, this makes it easier to see what changed in the git diff.
pkg.dependencies = Object.assign((_dependencies1 = pkg.dependencies) != null ? _dependencies1 : {}, combinedDependencies);
}
return {
changedDependencies
};
}
function diffKeys(a, b) {
return Object.keys(a).filter((key)=>a[key] !== b[key]);
}
function createDependenciesMap(dependencies) {
if (typeof dependencies !== "object") {
throw new Error(`Dependency map is invalid, expected object but got ${typeof dependencies}`);
} else if (!dependencies) {
return {};
}
const outputMap = {};
for (const key of Object.keys(dependencies)){
const value = dependencies[key];
if (typeof value === "string") {
outputMap[key] = value;
} else {
throw new Error(`Dependency for key \`${key}\` should be a \`string\`, instead got: \`{ ${key}: ${JSON.stringify(value)} }\``);
}
}
return outputMap;
}
/**
* Update package.json scripts - `npm start` should default to `expo
* start --dev-client` rather than `expo start` after prebuilding, for example.
*/ function updatePkgScripts({ pkg }) {
var ref, ref1;
let hasChanged = false;
if (!pkg.scripts) {
pkg.scripts = {};
}
if (!((ref = pkg.scripts.android) == null ? void 0 : ref.includes("run"))) {
pkg.scripts.android = "expo run:android";
hasChanged = true;
}
if (!((ref1 = pkg.scripts.ios) == null ? void 0 : ref1.includes("run"))) {
pkg.scripts.ios = "expo run:ios";
hasChanged = true;
}
return hasChanged;
}
function normalizeDependencyMap(deps) {
return Object.keys(deps).map((dependency)=>`${dependency}@${deps[dependency]}`).sort();
}
function hashForDependencyMap(deps = {}) {
const depsList = normalizeDependencyMap(deps);
const depsString = depsList.join("\n");
return createFileHash(depsString);
}
function createFileHash(contents) {
// this doesn't need to be secure, the shorter the better.
return _crypto().default.createHash("sha1").update(contents).digest("hex");
}
/**
* Determine if two semver ranges are overlapping or intersecting.
* This is a safe version of `semver.intersects` that does not throw.
*/ function versionRangesIntersect(rangeA, rangeB) {
try {
return (0, _semver().intersects)(rangeA, rangeB);
} catch {
return false;
}
}
//# sourceMappingURL=updatePackageJson.js.map
;