From 93961d49194a2d4791cf330aa6494f18894a6d45 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Thu, 26 Sep 2024 13:50:08 +0200 Subject: [PATCH 1/8] fix(config): don't overwrite `react-native.config.js` if it may be configured --- scripts/configure.mjs | 37 ++++++++++++++++++++--- test/configure/writeAllFiles.test.ts | 44 +++++++++++++--------------- 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/scripts/configure.mjs b/scripts/configure.mjs index c27335f61..e30c8274d 100755 --- a/scripts/configure.mjs +++ b/scripts/configure.mjs @@ -108,6 +108,20 @@ export function mergeConfig(lhs, rhs) { }; } +/** + * Returns whether `react-native.config.js` needs to be updated. + * @param {string} packagePath + * @returns {boolean} + */ +function shouldUpdateReactNativeConfig(packagePath, fs = nodefs) { + const configPath = path.join(packagePath, "react-native.config.js"); + const config = readTextFile(configPath, fs); + return ( + !/["'`]react-native-test-app["'`]/.test(config) || + !config.includes("configureProjects") + ); +} + /** * Sort the keys in specified object. * @param {Record} obj @@ -609,15 +623,14 @@ export function updatePackageManifest( /** * Writes all specified files to disk. - * @param {Configuration["files"]} files + * @param {[string, string | FileCopy][]} files * @param {string} destination * @returns {Promise} */ export function writeAllFiles(files, destination, fs = nodefs.promises) { const options = { recursive: true, mode: 0o755 }; return Promise.all( - Object.keys(files).map(async (filename) => { - const content = files[filename]; + files.map(async ([filename, content]) => { if (!content) { return; } @@ -665,8 +678,24 @@ export function configure(params, fs = nodefs) { } const { files, oldFiles } = config; + const templateFiles = Object.entries(files).filter(([filename]) => { + switch (filename) { + case "react-native.config.js": { + const needsUpdate = shouldUpdateReactNativeConfig(packagePath); + if (!needsUpdate) { + warn( + `skipped modifying '${filename}' because it may already be configured for 'react-native-test-app'` + ); + } + return needsUpdate; + } + + default: + return true; + } + }); - writeAllFiles(files, packagePath).then(() => { + writeAllFiles(templateFiles, packagePath).then(() => { const packageManifest = path.join(packagePath, "package.json"); if (!fs.existsSync(packageManifest)) { // We cannot assume that the app itself is an npm package. Some libraries diff --git a/test/configure/writeAllFiles.test.ts b/test/configure/writeAllFiles.test.ts index 4ce1f9279..9373cdeb9 100644 --- a/test/configure/writeAllFiles.test.ts +++ b/test/configure/writeAllFiles.test.ts @@ -17,11 +17,11 @@ describe("writeAllFiles()", () => { it("writes all files to disk", async () => { setMockFiles({ "file-on-disk": "0" }); await writeAllFiles( - { - file0: { source: "file-on-disk" }, - file1: "1", - file2: "2", - }, + [ + ["file0", { source: "file-on-disk" }], + ["file1", "1"], + ["file2", "2"], + ], "test" ); @@ -32,11 +32,11 @@ describe("writeAllFiles()", () => { it("ignores files with no content", async () => { await writeAllFiles( - { - file1: "1", - file2: "2", - file3: "", - }, + [ + ["file1", "1"], + ["file2", "2"], + ["file3", ""], + ], "." ); @@ -48,25 +48,23 @@ describe("writeAllFiles()", () => { it("rethrows write exceptions", async () => { await rejects( writeAllFiles( - { - file0: { - // This entry will throw an exception - source: "Bad Arnold movies.txt", - }, - file1: "1", - file2: "2", - }, + [ + // This entry will throw an exception + ["file0", { source: "Bad Arnold movies.txt" }], + ["file1", "1"], + ["file2", "2"], + ], "." ) ); await rejects( writeAllFiles( - { - file1: "1", - file2: "2", - "": "3", // This entry will throw an exception - }, + [ + ["file1", "1"], + ["file2", "2"], + ["", "3"], // This entry will throw an exception + ], "." ) ); From 508cfb70829e4b325035a195cd9725617e486ee5 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:22:05 +0200 Subject: [PATCH 2/8] fixup! fix(config): don't overwrite `react-native.config.js` if it may be configured --- scripts/configure.mjs | 12 ++++++++---- test/android/gradle.ts | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/scripts/configure.mjs b/scripts/configure.mjs index e30c8274d..758be387f 100755 --- a/scripts/configure.mjs +++ b/scripts/configure.mjs @@ -110,11 +110,14 @@ export function mergeConfig(lhs, rhs) { /** * Returns whether `react-native.config.js` needs to be updated. - * @param {string} packagePath + * @param {string} configPath * @returns {boolean} */ -function shouldUpdateReactNativeConfig(packagePath, fs = nodefs) { - const configPath = path.join(packagePath, "react-native.config.js"); +function shouldUpdateReactNativeConfig(configPath, fs = nodefs) { + if (!fs.existsSync(configPath)) { + return true; + } + const config = readTextFile(configPath, fs); return ( !/["'`]react-native-test-app["'`]/.test(config) || @@ -681,7 +684,8 @@ export function configure(params, fs = nodefs) { const templateFiles = Object.entries(files).filter(([filename]) => { switch (filename) { case "react-native.config.js": { - const needsUpdate = shouldUpdateReactNativeConfig(packagePath); + const configPath = path.join(packagePath, filename); + const needsUpdate = shouldUpdateReactNativeConfig(configPath); if (!needsUpdate) { warn( `skipped modifying '${filename}' because it may already be configured for 'react-native-test-app'` diff --git a/test/android/gradle.ts b/test/android/gradle.ts index 154979dc1..32ebf4ee3 100644 --- a/test/android/gradle.ts +++ b/test/android/gradle.ts @@ -73,7 +73,7 @@ async function makeProject( init: true, }); - await writeAllFiles(files, packagePath); + await writeAllFiles(Object.entries(files), packagePath); try { await fsp.symlink( From 234a6169987c00b90a46fb411647d2c8e49bf5ab Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:41:38 +0200 Subject: [PATCH 3/8] add tests --- scripts/configure.mjs | 2 +- .../shouldUpdateReactNativeConfig.test.ts | 57 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 test/configure/shouldUpdateReactNativeConfig.test.ts diff --git a/scripts/configure.mjs b/scripts/configure.mjs index 758be387f..242ab36bd 100755 --- a/scripts/configure.mjs +++ b/scripts/configure.mjs @@ -113,7 +113,7 @@ export function mergeConfig(lhs, rhs) { * @param {string} configPath * @returns {boolean} */ -function shouldUpdateReactNativeConfig(configPath, fs = nodefs) { +export function shouldUpdateReactNativeConfig(configPath, fs = nodefs) { if (!fs.existsSync(configPath)) { return true; } diff --git a/test/configure/shouldUpdateReactNativeConfig.test.ts b/test/configure/shouldUpdateReactNativeConfig.test.ts new file mode 100644 index 000000000..b425ff1fb --- /dev/null +++ b/test/configure/shouldUpdateReactNativeConfig.test.ts @@ -0,0 +1,57 @@ +import { ok } from "node:assert/strict"; +import { afterEach, describe, it } from "node:test"; +import { shouldUpdateReactNativeConfig as shouldUpdateReactNativeConfigActual } from "../../scripts/configure.mjs"; +import { fs, setMockFiles } from "../fs.mock.js"; + +describe("shouldUpdateReactNativeConfig()", () => { + const rnConfigFile = "react-native.config.js"; + + const shouldUpdateReactNativeConfig = () => + shouldUpdateReactNativeConfigActual(rnConfigFile, fs); + + afterEach(() => setMockFiles()); + + it("returns true if `react-native.config.js` does not exist", () => { + ok(shouldUpdateReactNativeConfig()); + }); + + it("returns true if `react-native.config.js` is not configured", () => { + setMockFiles({ [rnConfigFile]: "module.exports = {};" }); + + ok(shouldUpdateReactNativeConfig()); + }); + + it("returns true if `react-native-test-app` is mentioned but not used", () => { + setMockFiles({ + [rnConfigFile]: `// TODO: Use 'react-native-test-app' +module.exports = {}; +`, + }); + + ok(shouldUpdateReactNativeConfig()); + }); + + it("returns false if `react-native-test-app` is configured", () => { + setMockFiles({ + [rnConfigFile]: `const { configureProjects } = require("react-native-test-app"); +module.exports = { + project: configureProjects({ + android: { + sourceDir: "android", + }, + ios: { + sourceDir: "ios", + }, + }), + dependencies: { + MyPackage: { + root: require("node:path").dirname(__dirname), + }, + }, +}; +`, + }); + + ok(!shouldUpdateReactNativeConfig()); + }); +}); From 31342bb7495335c5d70f8c63ea3b8d0ca6315d6d Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Fri, 27 Sep 2024 09:50:07 +0200 Subject: [PATCH 4/8] Revert "add tests" This reverts commit 234a6169987c00b90a46fb411647d2c8e49bf5ab. --- scripts/configure.mjs | 2 +- .../shouldUpdateReactNativeConfig.test.ts | 57 ------------------- 2 files changed, 1 insertion(+), 58 deletions(-) delete mode 100644 test/configure/shouldUpdateReactNativeConfig.test.ts diff --git a/scripts/configure.mjs b/scripts/configure.mjs index 242ab36bd..758be387f 100755 --- a/scripts/configure.mjs +++ b/scripts/configure.mjs @@ -113,7 +113,7 @@ export function mergeConfig(lhs, rhs) { * @param {string} configPath * @returns {boolean} */ -export function shouldUpdateReactNativeConfig(configPath, fs = nodefs) { +function shouldUpdateReactNativeConfig(configPath, fs = nodefs) { if (!fs.existsSync(configPath)) { return true; } diff --git a/test/configure/shouldUpdateReactNativeConfig.test.ts b/test/configure/shouldUpdateReactNativeConfig.test.ts deleted file mode 100644 index b425ff1fb..000000000 --- a/test/configure/shouldUpdateReactNativeConfig.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { ok } from "node:assert/strict"; -import { afterEach, describe, it } from "node:test"; -import { shouldUpdateReactNativeConfig as shouldUpdateReactNativeConfigActual } from "../../scripts/configure.mjs"; -import { fs, setMockFiles } from "../fs.mock.js"; - -describe("shouldUpdateReactNativeConfig()", () => { - const rnConfigFile = "react-native.config.js"; - - const shouldUpdateReactNativeConfig = () => - shouldUpdateReactNativeConfigActual(rnConfigFile, fs); - - afterEach(() => setMockFiles()); - - it("returns true if `react-native.config.js` does not exist", () => { - ok(shouldUpdateReactNativeConfig()); - }); - - it("returns true if `react-native.config.js` is not configured", () => { - setMockFiles({ [rnConfigFile]: "module.exports = {};" }); - - ok(shouldUpdateReactNativeConfig()); - }); - - it("returns true if `react-native-test-app` is mentioned but not used", () => { - setMockFiles({ - [rnConfigFile]: `// TODO: Use 'react-native-test-app' -module.exports = {}; -`, - }); - - ok(shouldUpdateReactNativeConfig()); - }); - - it("returns false if `react-native-test-app` is configured", () => { - setMockFiles({ - [rnConfigFile]: `const { configureProjects } = require("react-native-test-app"); -module.exports = { - project: configureProjects({ - android: { - sourceDir: "android", - }, - ios: { - sourceDir: "ios", - }, - }), - dependencies: { - MyPackage: { - root: require("node:path").dirname(__dirname), - }, - }, -}; -`, - }); - - ok(!shouldUpdateReactNativeConfig()); - }); -}); From ec299c2597ff2227884a974eef44194d9f8e7f53 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Fri, 27 Sep 2024 09:50:16 +0200 Subject: [PATCH 5/8] Revert "fixup! fix(config): don't overwrite `react-native.config.js` if it may be configured" This reverts commit 508cfb70829e4b325035a195cd9725617e486ee5. --- scripts/configure.mjs | 12 ++++-------- test/android/gradle.ts | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/scripts/configure.mjs b/scripts/configure.mjs index 758be387f..e30c8274d 100755 --- a/scripts/configure.mjs +++ b/scripts/configure.mjs @@ -110,14 +110,11 @@ export function mergeConfig(lhs, rhs) { /** * Returns whether `react-native.config.js` needs to be updated. - * @param {string} configPath + * @param {string} packagePath * @returns {boolean} */ -function shouldUpdateReactNativeConfig(configPath, fs = nodefs) { - if (!fs.existsSync(configPath)) { - return true; - } - +function shouldUpdateReactNativeConfig(packagePath, fs = nodefs) { + const configPath = path.join(packagePath, "react-native.config.js"); const config = readTextFile(configPath, fs); return ( !/["'`]react-native-test-app["'`]/.test(config) || @@ -684,8 +681,7 @@ export function configure(params, fs = nodefs) { const templateFiles = Object.entries(files).filter(([filename]) => { switch (filename) { case "react-native.config.js": { - const configPath = path.join(packagePath, filename); - const needsUpdate = shouldUpdateReactNativeConfig(configPath); + const needsUpdate = shouldUpdateReactNativeConfig(packagePath); if (!needsUpdate) { warn( `skipped modifying '${filename}' because it may already be configured for 'react-native-test-app'` diff --git a/test/android/gradle.ts b/test/android/gradle.ts index 32ebf4ee3..154979dc1 100644 --- a/test/android/gradle.ts +++ b/test/android/gradle.ts @@ -73,7 +73,7 @@ async function makeProject( init: true, }); - await writeAllFiles(Object.entries(files), packagePath); + await writeAllFiles(files, packagePath); try { await fsp.symlink( From 2096f1b4b078af8f4672a27c9942fd5fecb35741 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Fri, 27 Sep 2024 09:50:23 +0200 Subject: [PATCH 6/8] Revert "fix(config): don't overwrite `react-native.config.js` if it may be configured" This reverts commit 93961d49194a2d4791cf330aa6494f18894a6d45. --- scripts/configure.mjs | 37 +++-------------------- test/configure/writeAllFiles.test.ts | 44 +++++++++++++++------------- 2 files changed, 27 insertions(+), 54 deletions(-) diff --git a/scripts/configure.mjs b/scripts/configure.mjs index e30c8274d..c27335f61 100755 --- a/scripts/configure.mjs +++ b/scripts/configure.mjs @@ -108,20 +108,6 @@ export function mergeConfig(lhs, rhs) { }; } -/** - * Returns whether `react-native.config.js` needs to be updated. - * @param {string} packagePath - * @returns {boolean} - */ -function shouldUpdateReactNativeConfig(packagePath, fs = nodefs) { - const configPath = path.join(packagePath, "react-native.config.js"); - const config = readTextFile(configPath, fs); - return ( - !/["'`]react-native-test-app["'`]/.test(config) || - !config.includes("configureProjects") - ); -} - /** * Sort the keys in specified object. * @param {Record} obj @@ -623,14 +609,15 @@ export function updatePackageManifest( /** * Writes all specified files to disk. - * @param {[string, string | FileCopy][]} files + * @param {Configuration["files"]} files * @param {string} destination * @returns {Promise} */ export function writeAllFiles(files, destination, fs = nodefs.promises) { const options = { recursive: true, mode: 0o755 }; return Promise.all( - files.map(async ([filename, content]) => { + Object.keys(files).map(async (filename) => { + const content = files[filename]; if (!content) { return; } @@ -678,24 +665,8 @@ export function configure(params, fs = nodefs) { } const { files, oldFiles } = config; - const templateFiles = Object.entries(files).filter(([filename]) => { - switch (filename) { - case "react-native.config.js": { - const needsUpdate = shouldUpdateReactNativeConfig(packagePath); - if (!needsUpdate) { - warn( - `skipped modifying '${filename}' because it may already be configured for 'react-native-test-app'` - ); - } - return needsUpdate; - } - - default: - return true; - } - }); - writeAllFiles(templateFiles, packagePath).then(() => { + writeAllFiles(files, packagePath).then(() => { const packageManifest = path.join(packagePath, "package.json"); if (!fs.existsSync(packageManifest)) { // We cannot assume that the app itself is an npm package. Some libraries diff --git a/test/configure/writeAllFiles.test.ts b/test/configure/writeAllFiles.test.ts index 9373cdeb9..4ce1f9279 100644 --- a/test/configure/writeAllFiles.test.ts +++ b/test/configure/writeAllFiles.test.ts @@ -17,11 +17,11 @@ describe("writeAllFiles()", () => { it("writes all files to disk", async () => { setMockFiles({ "file-on-disk": "0" }); await writeAllFiles( - [ - ["file0", { source: "file-on-disk" }], - ["file1", "1"], - ["file2", "2"], - ], + { + file0: { source: "file-on-disk" }, + file1: "1", + file2: "2", + }, "test" ); @@ -32,11 +32,11 @@ describe("writeAllFiles()", () => { it("ignores files with no content", async () => { await writeAllFiles( - [ - ["file1", "1"], - ["file2", "2"], - ["file3", ""], - ], + { + file1: "1", + file2: "2", + file3: "", + }, "." ); @@ -48,23 +48,25 @@ describe("writeAllFiles()", () => { it("rethrows write exceptions", async () => { await rejects( writeAllFiles( - [ - // This entry will throw an exception - ["file0", { source: "Bad Arnold movies.txt" }], - ["file1", "1"], - ["file2", "2"], - ], + { + file0: { + // This entry will throw an exception + source: "Bad Arnold movies.txt", + }, + file1: "1", + file2: "2", + }, "." ) ); await rejects( writeAllFiles( - [ - ["file1", "1"], - ["file2", "2"], - ["", "3"], // This entry will throw an exception - ], + { + file1: "1", + file2: "2", + "": "3", // This entry will throw an exception + }, "." ) ); From a861e8792c988a2ef124f99378e0f2a8e4149480 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Fri, 27 Sep 2024 09:55:57 +0200 Subject: [PATCH 7/8] better message --- scripts/configure.mjs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/configure.mjs b/scripts/configure.mjs index c27335f61..9f6a7ace4 100755 --- a/scripts/configure.mjs +++ b/scripts/configure.mjs @@ -552,7 +552,8 @@ export function isDestructive(packagePath, { files, oldFiles }, fs = nodefs) { if (modified.length > 0 || removed.length > 0) { if (modified.length > 0) { - warn("The following files will be overwritten:"); + const reset = colors.bold("reset"); + warn(`The following files will be ${reset} to their original state:`); modified.sort().forEach((file) => warn(file, " ")); } if (removed.length > 0) { @@ -657,10 +658,11 @@ export function configure(params, fs = nodefs) { const config = gatherConfig(params); if (!force && isDestructive(packagePath, config)) { - error("Destructive file operations are required."); - console.log( - `Re-run with ${colors.bold("--force")} if you're fine with this.` + error( + "Some files will be reset and/or removed: You may have to manually restore any customizations to get the app working again (for more details, see https://github.com/microsoft/react-native-test-app/wiki/Updating#reconfiguringresetting-rnta)" ); + const forceFlag = colors.bold("--force"); + console.log(`Re-run with ${forceFlag} if you're fine with this.`); return 1; } From 66701b288b90af489e8705520f414fcd77a4df39 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Mon, 30 Sep 2024 10:23:22 +0200 Subject: [PATCH 8/8] change wording to include templates --- scripts/configure.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/configure.mjs b/scripts/configure.mjs index 9f6a7ace4..0228af0fd 100755 --- a/scripts/configure.mjs +++ b/scripts/configure.mjs @@ -659,7 +659,7 @@ export function configure(params, fs = nodefs) { if (!force && isDestructive(packagePath, config)) { error( - "Some files will be reset and/or removed: You may have to manually restore any customizations to get the app working again (for more details, see https://github.com/microsoft/react-native-test-app/wiki/Updating#reconfiguringresetting-rnta)" + "Some files will be reset and/or removed: You may have to manually restore your own or your template's customizations to get the app working again (for more details, see https://github.com/microsoft/react-native-test-app/wiki/Updating#reconfiguringresetting-rnta)" ); const forceFlag = colors.bold("--force"); console.log(`Re-run with ${forceFlag} if you're fine with this.`);