fix: warn and skip unsupported versions (#373)
This commit is contained in:
Родитель
d115d0138d
Коммит
9a8486a16e
|
@ -3615,6 +3615,7 @@ react-native-test-app@../:
|
|||
chalk "^4.1.0"
|
||||
prompts "^2.4.0"
|
||||
rimraf "^3.0.0"
|
||||
semver "^7.3.5"
|
||||
yargs "^16.0.0"
|
||||
|
||||
react-native-windows@^0.63.32:
|
||||
|
@ -3922,7 +3923,7 @@ semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
|
|||
resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
|
||||
semver@^7.1.3:
|
||||
semver@^7.1.3, semver@^7.3.5:
|
||||
version "7.3.5"
|
||||
resolved "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
|
||||
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
"chalk": "^4.1.0",
|
||||
"prompts": "^2.4.0",
|
||||
"rimraf": "^3.0.0",
|
||||
"semver": "^7.3.5",
|
||||
"yargs": "^16.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
@ -84,6 +85,7 @@
|
|||
"@types/node": "^12.0.0",
|
||||
"@types/prompts": "^2.0.0",
|
||||
"@types/rimraf": "^3.0.0",
|
||||
"@types/semver": "^7.3.6",
|
||||
"eslint": "^7.10.0",
|
||||
"eslint-plugin-jest": "^24.0.0",
|
||||
"eslint-plugin-prettier": "^3.1.4",
|
||||
|
@ -149,7 +151,9 @@
|
|||
]
|
||||
},
|
||||
"release": {
|
||||
"branches": ["trunk"],
|
||||
"branches": [
|
||||
"trunk"
|
||||
],
|
||||
"tagFormat": "${version}"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,11 +70,16 @@ module.exports = {
|
|||
{
|
||||
name: "init-test-app",
|
||||
description: "Initializes a new test app project",
|
||||
func: (argv, config, { destination, name, platform }) => {
|
||||
require("./scripts/configure").configure({
|
||||
func: (_argv, _config, { destination, name, platform }) => {
|
||||
const {
|
||||
configure,
|
||||
getTargetReactNativeVersion,
|
||||
} = require("./scripts/configure");
|
||||
configure({
|
||||
name,
|
||||
packagePath: destination,
|
||||
testAppPath: __dirname,
|
||||
targetVersion: getTargetReactNativeVersion(),
|
||||
platforms: sanitizePlatformChoice(platform),
|
||||
flatten: true,
|
||||
force: true,
|
||||
|
@ -86,7 +91,7 @@ module.exports = {
|
|||
name: "--destination [string]",
|
||||
description:
|
||||
"Path to the directory where the test app should be created",
|
||||
default: process.cwd(),
|
||||
default: require("path").join(process.cwd(), "test-app"),
|
||||
},
|
||||
{
|
||||
name: "--name [string]",
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
const chalk = require("chalk");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const semver = require("semver");
|
||||
|
||||
/**
|
||||
* @typedef {{ source: string; }} FileCopy;
|
||||
|
@ -19,6 +20,7 @@ const path = require("path");
|
|||
* oldFiles: string[];
|
||||
* scripts: Record<string, string>;
|
||||
* dependencies: Record<string, string>;
|
||||
* getDependencies?: (params: ConfigureParams) => Record<string, string> | undefined;
|
||||
* }} Configuration;
|
||||
*
|
||||
* @typedef {{
|
||||
|
@ -35,6 +37,7 @@ const path = require("path");
|
|||
* name: string;
|
||||
* packagePath: string;
|
||||
* testAppPath: string;
|
||||
* targetVersion: string;
|
||||
* platforms: Platform[];
|
||||
* flatten: boolean;
|
||||
* force: boolean;
|
||||
|
@ -177,6 +180,29 @@ function warn(message, tag = "[!]") {
|
|||
console.warn(chalk.yellow(`${tag} ${message}`));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns platform package at target version if it satisfies version range.
|
||||
* @param {string} packageName
|
||||
* @param {string} targetVersion
|
||||
* @param {string} versionRange
|
||||
* @returns {Record<string, string> | undefined}
|
||||
*/
|
||||
function getPlatformPackage(packageName, targetVersion, versionRange) {
|
||||
const v = semver.coerce(targetVersion);
|
||||
if (!v) {
|
||||
throw new Error(`Invalid ${packageName} version: ${targetVersion}`);
|
||||
}
|
||||
|
||||
if (!semver.satisfies(v.version, versionRange)) {
|
||||
warn(
|
||||
`${packageName}@${v.major}.${v.minor} cannot be added because it does not exist or is unsupported`
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return { [packageName]: `^${v.major}.${v.minor}.0` };
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate `react-native.config.js` for specified parameters.
|
||||
* @param {ConfigureParams} params
|
||||
|
@ -334,6 +360,7 @@ const getConfig = (() => {
|
|||
start: "react-native start",
|
||||
},
|
||||
dependencies: {},
|
||||
getDependencies: () => ({}),
|
||||
},
|
||||
android: {
|
||||
files: {
|
||||
|
@ -416,6 +443,7 @@ const getConfig = (() => {
|
|||
"mkdirp dist/res && react-native bundle --entry-file index.js --platform android --dev true --bundle-output dist/main.android.jsbundle --assets-dest dist/res",
|
||||
},
|
||||
dependencies: {},
|
||||
getDependencies: () => ({}),
|
||||
},
|
||||
ios: {
|
||||
files: {
|
||||
|
@ -445,6 +473,7 @@ const getConfig = (() => {
|
|||
ios: "react-native run-ios",
|
||||
},
|
||||
dependencies: {},
|
||||
getDependencies: () => ({}),
|
||||
},
|
||||
macos: {
|
||||
files: {
|
||||
|
@ -468,8 +497,13 @@ const getConfig = (() => {
|
|||
"mkdirp dist && react-native bundle --entry-file index.js --platform macos --dev true --bundle-output dist/main.macos.jsbundle --assets-dest dist",
|
||||
macos: `react-native run-macos --scheme ${name}`,
|
||||
},
|
||||
dependencies: {
|
||||
"react-native-macos": "^0.63.0",
|
||||
dependencies: {},
|
||||
getDependencies: ({ targetVersion }) => {
|
||||
return getPlatformPackage(
|
||||
"react-native-macos",
|
||||
targetVersion,
|
||||
"^0.0.0-0 || >=0.60.0 <0.64"
|
||||
);
|
||||
},
|
||||
},
|
||||
windows: {
|
||||
|
@ -484,8 +518,13 @@ const getConfig = (() => {
|
|||
"mkdirp dist && react-native bundle --entry-file index.js --platform windows --dev true --bundle-output dist/main.windows.bundle --assets-dest dist",
|
||||
windows: `react-native run-windows --sln windows/${name}.sln`,
|
||||
},
|
||||
dependencies: {
|
||||
"react-native-windows": "^0.63.0",
|
||||
dependencies: {},
|
||||
getDependencies: ({ targetVersion }) => {
|
||||
return getPlatformPackage(
|
||||
"react-native-windows",
|
||||
targetVersion,
|
||||
"^0.0.0-0 || >=0.62.0 <0.66"
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -502,26 +541,40 @@ const getConfig = (() => {
|
|||
function gatherConfig(params) {
|
||||
const { flatten, platforms } = params;
|
||||
const config = (() => {
|
||||
if (platforms.length === 1 && flatten) {
|
||||
return getConfig(params, platforms[0]);
|
||||
}
|
||||
const shouldFlatten = platforms.length === 1 && flatten;
|
||||
|
||||
return platforms.reduce(
|
||||
(config, platform) => {
|
||||
const platformConfig = getConfig(params, platform);
|
||||
const { getDependencies, ...platformConfig } = getConfig(
|
||||
params,
|
||||
platform
|
||||
);
|
||||
|
||||
const dependencies = getDependencies && getDependencies(params);
|
||||
if (!dependencies) {
|
||||
return config;
|
||||
}
|
||||
|
||||
return mergeConfig(config, {
|
||||
...platformConfig,
|
||||
files: Object.fromEntries(
|
||||
// Map each file into its platform specific folder, e.g.
|
||||
// `Podfile` -> `iod/Podfile`
|
||||
Object.entries(platformConfig.files).map(([filename, content]) => [
|
||||
path.join(platform, filename),
|
||||
content,
|
||||
])
|
||||
),
|
||||
oldFiles: platformConfig.oldFiles.map((file) => {
|
||||
return path.join(platform, file);
|
||||
}),
|
||||
dependencies,
|
||||
files: shouldFlatten
|
||||
? platformConfig.files
|
||||
: Object.fromEntries(
|
||||
// Map each file into its platform specific folder, e.g.
|
||||
// `Podfile` -> `ios/Podfile`
|
||||
Object.entries(platformConfig.files).map(
|
||||
([filename, content]) => [
|
||||
path.join(platform, filename),
|
||||
content,
|
||||
]
|
||||
)
|
||||
),
|
||||
oldFiles: shouldFlatten
|
||||
? platformConfig.oldFiles
|
||||
: platformConfig.oldFiles.map((file) => {
|
||||
return path.join(platform, file);
|
||||
}),
|
||||
});
|
||||
},
|
||||
/** @type {Configuration} */ ({
|
||||
|
@ -532,6 +585,16 @@ function gatherConfig(params) {
|
|||
})
|
||||
);
|
||||
})();
|
||||
|
||||
if (
|
||||
Object.keys(config.scripts).length === 0 &&
|
||||
Object.keys(config.dependencies).length === 0 &&
|
||||
Object.keys(config.files).length === 0 &&
|
||||
config.oldFiles.length === 0
|
||||
) {
|
||||
return config;
|
||||
}
|
||||
|
||||
return mergeConfig(config, getConfig(params, "common"));
|
||||
}
|
||||
|
||||
|
@ -553,6 +616,16 @@ function getAppName(packagePath) {
|
|||
return "ReactTestApp";
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the version of React Native to target.
|
||||
* @returns {string}
|
||||
*/
|
||||
function getTargetReactNativeVersion() {
|
||||
const manifestPath = require.resolve("react-native/package.json");
|
||||
const { version } = readJSONFile(manifestPath);
|
||||
return /** @type {string} */ (version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether destructive operations will be required.
|
||||
* @param {string} packagePath
|
||||
|
@ -579,11 +652,11 @@ function isDestructive(packagePath, { files, oldFiles }) {
|
|||
if (modified.length > 0 || removed.length > 0) {
|
||||
if (modified.length > 0) {
|
||||
warn("The following files will be overwritten:");
|
||||
modified.sort().forEach((file) => warn(file, " "));
|
||||
modified.sort().forEach((file) => warn(file, " "));
|
||||
}
|
||||
if (removed.length > 0) {
|
||||
warn("The following files will be removed:");
|
||||
removed.sort().forEach((file) => warn(file, " "));
|
||||
removed.sort().forEach((file) => warn(file, " "));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -725,6 +798,7 @@ function configure(params) {
|
|||
if (require.main === module) {
|
||||
/** @type {Platform[]} */
|
||||
const platformChoices = ["android", "ios", "macos", "windows"];
|
||||
const targetVersion = getTargetReactNativeVersion();
|
||||
|
||||
require("yargs").usage(
|
||||
"$0 [options]",
|
||||
|
@ -772,6 +846,7 @@ if (require.main === module) {
|
|||
name: typeof name === "string" && name ? name : getAppName(packagePath),
|
||||
packagePath,
|
||||
testAppPath: path.resolve(__dirname, ".."),
|
||||
targetVersion,
|
||||
platforms,
|
||||
flatten,
|
||||
force,
|
||||
|
@ -789,6 +864,8 @@ exports["error"] = error;
|
|||
exports["gatherConfig"] = gatherConfig;
|
||||
exports["getAppName"] = getAppName;
|
||||
exports["getConfig"] = getConfig;
|
||||
exports["getPlatformPackage"] = getPlatformPackage;
|
||||
exports["getTargetReactNativeVersion"] = getTargetReactNativeVersion;
|
||||
exports["isDestructive"] = isDestructive;
|
||||
exports["isInstalled"] = isInstalled;
|
||||
exports["join"] = join;
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
// @ts-check
|
||||
|
||||
(async () => {
|
||||
const { configure, getTargetReactNativeVersion } = require("./configure");
|
||||
const targetVersion = getTargetReactNativeVersion();
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
* name?: string;
|
||||
|
@ -47,12 +50,12 @@
|
|||
}
|
||||
|
||||
const path = require("path");
|
||||
const { configure } = require("./configure");
|
||||
|
||||
const result = configure({
|
||||
name,
|
||||
packagePath,
|
||||
testAppPath: path.resolve(__dirname, ".."),
|
||||
targetVersion,
|
||||
platforms,
|
||||
flatten: true,
|
||||
force: true,
|
||||
|
|
|
@ -10,8 +10,20 @@ describe("getConfig()", () => {
|
|||
const { mockParams } = require("./mockParams");
|
||||
const { getConfig } = require("../../scripts/configure");
|
||||
|
||||
/**
|
||||
* Gets the list of dependencies from specified config.
|
||||
* @param {import("../../scripts/configure").Configuration} config
|
||||
* @param {import("../../scripts/configure").ConfigureParams} params
|
||||
* @returns {string[] | undefined}
|
||||
*/
|
||||
function getDependencies({ getDependencies }, params) {
|
||||
const dependencies = getDependencies && getDependencies(params);
|
||||
return dependencies && Object.keys(dependencies);
|
||||
}
|
||||
|
||||
test("returns common scripts and files", () => {
|
||||
const config = getConfig(mockParams(), "common");
|
||||
const params = mockParams();
|
||||
const config = getConfig(params, "common");
|
||||
|
||||
expect(Object.keys(config.files).sort()).toEqual([
|
||||
".watchmanconfig",
|
||||
|
@ -21,11 +33,12 @@ describe("getConfig()", () => {
|
|||
]);
|
||||
expect(config.oldFiles).toEqual([]);
|
||||
expect(Object.keys(config.scripts).sort()).toEqual(["start"]);
|
||||
expect(Object.keys(config.dependencies)).toEqual([]);
|
||||
expect(getDependencies(config, params)).toEqual([]);
|
||||
});
|
||||
|
||||
test("returns more common scripts and files when initializing", () => {
|
||||
const config = getConfig(mockParams({ init: true }), "common");
|
||||
const params = mockParams({ init: true });
|
||||
const config = getConfig(params, "common");
|
||||
|
||||
expect(Object.keys(config.files).sort()).toEqual([
|
||||
".watchmanconfig",
|
||||
|
@ -39,17 +52,18 @@ describe("getConfig()", () => {
|
|||
]);
|
||||
expect(config.oldFiles).toEqual([]);
|
||||
expect(Object.keys(config.scripts).sort()).toEqual(["start"]);
|
||||
expect(Object.keys(config.dependencies)).toEqual([]);
|
||||
expect(getDependencies(config, params)).toEqual([]);
|
||||
});
|
||||
|
||||
test("returns Android specific scripts and additional files", () => {
|
||||
const config = getConfig(mockParams(), "android");
|
||||
const params = mockParams();
|
||||
const config = getConfig(params, "android");
|
||||
|
||||
expect(Object.keys(config.scripts).sort()).toEqual([
|
||||
"android",
|
||||
"build:android",
|
||||
]);
|
||||
expect(Object.keys(config.dependencies)).toEqual([]);
|
||||
expect(getDependencies(config, params)).toEqual([]);
|
||||
expect(Object.keys(config.files).sort()).toEqual([
|
||||
"build.gradle",
|
||||
"gradle.properties",
|
||||
|
@ -63,10 +77,11 @@ describe("getConfig()", () => {
|
|||
});
|
||||
|
||||
test("returns iOS specific scripts and additional files", () => {
|
||||
const config = getConfig(mockParams(), "ios");
|
||||
const params = mockParams();
|
||||
const config = getConfig(params, "ios");
|
||||
|
||||
expect(Object.keys(config.scripts).sort()).toEqual(["build:ios", "ios"]);
|
||||
expect(Object.keys(config.dependencies)).toEqual([]);
|
||||
expect(getDependencies(config, params)).toEqual([]);
|
||||
expect(Object.keys(config.files).sort()).toEqual(["Podfile"]);
|
||||
expect(config.oldFiles.sort()).toEqual([
|
||||
"Podfile.lock",
|
||||
|
@ -77,14 +92,15 @@ describe("getConfig()", () => {
|
|||
});
|
||||
|
||||
test("returns macOS specific scripts and additional files", () => {
|
||||
const config = getConfig(mockParams(), "macos");
|
||||
const params = mockParams();
|
||||
const config = getConfig(params, "macos");
|
||||
|
||||
expect(Object.keys(config.scripts).sort()).toEqual([
|
||||
"build:macos",
|
||||
"macos",
|
||||
]);
|
||||
expect(Object.keys(config.files).sort()).toEqual(["Podfile"]);
|
||||
expect(Object.keys(config.dependencies)).toEqual(["react-native-macos"]);
|
||||
expect(getDependencies(config, params)).toEqual(["react-native-macos"]);
|
||||
expect(config.oldFiles.sort()).toEqual([
|
||||
"Podfile.lock",
|
||||
"Pods",
|
||||
|
@ -94,13 +110,14 @@ describe("getConfig()", () => {
|
|||
});
|
||||
|
||||
test("returns Windows specific scripts and additional files", () => {
|
||||
const config = getConfig(mockParams(), "windows");
|
||||
const params = mockParams();
|
||||
const config = getConfig(params, "windows");
|
||||
|
||||
expect(Object.keys(config.scripts).sort()).toEqual([
|
||||
"build:windows",
|
||||
"windows",
|
||||
]);
|
||||
expect(Object.keys(config.dependencies)).toEqual(["react-native-windows"]);
|
||||
expect(getDependencies(config, params)).toEqual(["react-native-windows"]);
|
||||
expect(Object.keys(config.files).sort()).toEqual([]);
|
||||
expect(config.oldFiles.sort()).toEqual([
|
||||
"Test.sln",
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// Copyright (c) Microsoft Corporation
|
||||
//
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
// @ts-check
|
||||
|
||||
describe("getPlatformPackage()", () => {
|
||||
const { getPlatformPackage } = require("../../scripts/configure");
|
||||
|
||||
const consoleWarnSpy = jest.spyOn(global.console, "warn");
|
||||
|
||||
const name = "react-native-*";
|
||||
const versionRange = "^0.0.0-0 || >=0.60.0 <0.64";
|
||||
|
||||
afterEach(() => {
|
||||
consoleWarnSpy.mockReset();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test("returns dependency when target version is inside range", () => {
|
||||
["0.0.0-canary", "^0.0.0-canary"].forEach((targetVersion) => {
|
||||
const pkg = getPlatformPackage(name, targetVersion, versionRange);
|
||||
expect(pkg).toEqual({ [name]: "^0.0.0" });
|
||||
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
["0.63", "0.63.4", "^0.63", "^0.63.4"].forEach((targetVersion) => {
|
||||
const pkg = getPlatformPackage(name, targetVersion, versionRange);
|
||||
expect(pkg).toEqual({ [name]: "^0.63.0" });
|
||||
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
test("returns `undefined` when target version is outside range", () => {
|
||||
["0.59", "0.64"].forEach((targetVersion) => {
|
||||
const pkg = getPlatformPackage(name, targetVersion, versionRange);
|
||||
expect(pkg).toBeUndefined();
|
||||
expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
|
||||
|
||||
consoleWarnSpy.mockReset();
|
||||
});
|
||||
});
|
||||
|
||||
test("throws if target version is invalid", () => {
|
||||
expect(() => getPlatformPackage("", "version", "")).toThrow();
|
||||
});
|
||||
});
|
|
@ -17,6 +17,7 @@ function mockParams(overrides) {
|
|||
name: "Test",
|
||||
packagePath: "test",
|
||||
testAppPath: ".",
|
||||
targetVersion: "^0.63.4",
|
||||
platforms: ["android", "ios", "macos", "windows"],
|
||||
flatten: false,
|
||||
force: false,
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
"resolveJsonModule": true
|
||||
},
|
||||
"include": [
|
||||
"react-native.config.js",
|
||||
"scripts/**/*.js",
|
||||
"test/**/*.test.js",
|
||||
"windows/test-app.js"
|
||||
|
|
|
@ -1782,6 +1782,11 @@
|
|||
"@types/glob" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/semver@^7.3.6":
|
||||
version "7.3.6"
|
||||
resolved "https://registry.npmjs.org/@types/semver/-/semver-7.3.6.tgz#e9831776f4512a7ba6da53e71c26e5fb67882d63"
|
||||
integrity sha512-0caWDWmpCp0uifxFh+FaqK3CuZ2SkRR/ZRxAV5+zNdC3QVUi6wyOJnefhPvtNt8NQWXB5OA93BUvZsXpWat2Xw==
|
||||
|
||||
"@types/stack-utils@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
|
||||
|
|
Загрузка…
Ссылка в новой задаче