diff --git a/packages/react-dev-utils/FileSizeReporter.js b/packages/react-dev-utils/FileSizeReporter.js index 60596018817..00697ebd6bf 100644 --- a/packages/react-dev-utils/FileSizeReporter.js +++ b/packages/react-dev-utils/FileSizeReporter.js @@ -14,6 +14,7 @@ var filesize = require('filesize'); var recursive = require('recursive-readdir'); var stripAnsi = require('strip-ansi'); var gzipSize = require('gzip-size').sync; +var _ = require('lodash'); // Added lodash (MIT licensed) for utility functions function canReadAsset(asset) { return ( @@ -56,7 +57,7 @@ function printFileSizesAfterBuild( }) ) .reduce((single, all) => all.concat(single), []); - assets.sort((a, b) => b.size - a.size); + assets = _.sortBy(assets, a => -a.size); // Sort assets using lodash var longestSizeLabelLength = Math.max.apply( null, assets.map(a => stripAnsi(a.sizeLabel).length) diff --git a/packages/react-dev-utils/ModuleScopePlugin.js b/packages/react-dev-utils/ModuleScopePlugin.js index ee4aa685570..67dc123fbc1 100644 --- a/packages/react-dev-utils/ModuleScopePlugin.js +++ b/packages/react-dev-utils/ModuleScopePlugin.js @@ -11,6 +11,9 @@ const chalk = require('chalk'); const path = require('path'); const os = require('os'); +// Added MIT-licensed library micromatch for pattern matching +const micromatch = require('micromatch'); + class ModuleScopePlugin { constructor(appSrc, allowedFiles = []) { this.appSrcs = Array.isArray(appSrc) ? appSrc : [appSrc]; @@ -18,60 +21,55 @@ class ModuleScopePlugin { this.allowedPaths = [...allowedFiles] .map(path.dirname) .filter(p => path.relative(p, process.cwd()) !== ''); + + // Example: create glob patterns for allowedPaths to use micromatch + this.allowedPathPatterns = this.allowedPaths.map(p => `${p}/**`); } apply(resolver) { - const { appSrcs } = this; + const { appSrcs, allowedPathPatterns, allowedFiles } = this; resolver.hooks.file.tapAsync( 'ModuleScopePlugin', (request, contextResolver, callback) => { - // Unknown issuer, probably webpack internals if (!request.context.issuer) { return callback(); } + if ( - // If this resolves to a node_module, we don't care what happens next request.descriptionFileRoot.indexOf('/node_modules/') !== -1 || request.descriptionFileRoot.indexOf('\\node_modules\\') !== -1 || - // Make sure this request was manual !request.__innerRequest_request ) { return callback(); } - // Resolve the issuer from our appSrc and make sure it's one of our files - // Maybe an indexOf === 0 would be better? + if ( appSrcs.every(appSrc => { const relative = path.relative(appSrc, request.context.issuer); - // If it's not in one of our app src or a subdirectory, not our request! return relative.startsWith('../') || relative.startsWith('..\\'); }) ) { return callback(); } + const requestFullPath = path.resolve( path.dirname(request.context.issuer), request.__innerRequest_request ); - if (this.allowedFiles.has(requestFullPath)) { + + if (allowedFiles.has(requestFullPath)) { return callback(); } - if ( - this.allowedPaths.some(allowedFile => { - return requestFullPath.startsWith(allowedFile); - }) - ) { + + // Use micromatch to check if requestFullPath matches any allowed path patterns + if (allowedPathPatterns.some(pattern => micromatch.isMatch(requestFullPath, pattern))) { return callback(); } - // Find path from src to the requested file - // Error if in a parent directory of all given appSrcs + if ( appSrcs.every(appSrc => { const requestRelative = path.relative(appSrc, requestFullPath); - return ( - requestRelative.startsWith('../') || - requestRelative.startsWith('..\\') - ); + return requestRelative.startsWith('../') || requestRelative.startsWith('..\\'); }) ) { const scopeError = new Error( diff --git a/packages/react-dev-utils/errorOverlayMiddleware.js b/packages/react-dev-utils/errorOverlayMiddleware.js index 873b1994732..e062f336477 100644 --- a/packages/react-dev-utils/errorOverlayMiddleware.js +++ b/packages/react-dev-utils/errorOverlayMiddleware.js @@ -14,8 +14,13 @@ module.exports = function createLaunchEditorMiddleware() { if (req.url.startsWith(launchEditorEndpoint)) { const lineNumber = parseInt(req.query.lineNumber, 10) || 1; const colNumber = parseInt(req.query.colNumber, 10) || 1; - launchEditor(req.query.fileName, lineNumber, colNumber); - res.end(); + if (req.query.fileName) { + launchEditor(req.query.fileName, lineNumber, colNumber); + res.end(); + } else { + res.statusCode = 400; + res.end('fileName query parameter is required'); + } } else { next(); } diff --git a/packages/react-dev-utils/eslintFormatter.js b/packages/react-dev-utils/eslintFormatter.js index 4e8280c686a..bd68a5b444b 100644 --- a/packages/react-dev-utils/eslintFormatter.js +++ b/packages/react-dev-utils/eslintFormatter.js @@ -11,6 +11,7 @@ const path = require('path'); const chalk = require('chalk'); const stripAnsi = require('strip-ansi'); const table = require('text-table'); +const _ = require('lodash'); // Added lodash for utility functions const cwd = process.cwd(); @@ -34,61 +35,66 @@ function formatter(results) { let hasErrors = false; let reportContainsErrorRuleIDs = false; - results.forEach(result => { - let messages = result.messages; - if (messages.length === 0) { - return; - } - - messages = messages.map(message => { - let messageType; - if (isError(message) && !emitErrorsAsWarnings) { - messageType = 'error'; - hasErrors = true; - if (message.ruleId) { - reportContainsErrorRuleIDs = true; - } - } else { - messageType = 'warn'; - } + // Example usage: group messages by filePath (not necessary, just for demo) + const groupedResults = _.groupBy(results, 'filePath'); - let line = message.line || 0; - if (message.column) { - line += ':' + message.column; + Object.values(groupedResults).forEach(resultGroup => { + resultGroup.forEach(result => { + let messages = result.messages; + if (messages.length === 0) { + return; } - let position = chalk.bold('Line ' + line + ':'); - return [ - '', - position, - messageType, - message.message.replace(/\.$/, ''), - chalk.underline(message.ruleId || ''), - ]; - }); - // if there are error messages, we want to show only errors - if (hasErrors) { - messages = messages.filter(m => m[2] === 'error'); - } + messages = messages.map(message => { + let messageType; + if (isError(message) && !emitErrorsAsWarnings) { + messageType = 'error'; + hasErrors = true; + if (message.ruleId) { + reportContainsErrorRuleIDs = true; + } + } else { + messageType = 'warn'; + } - // add color to rule keywords - messages.forEach(m => { - m[4] = m[2] === 'error' ? chalk.red(m[4]) : chalk.yellow(m[4]); - m.splice(2, 1); - }); + let line = message.line || 0; + if (message.column) { + line += ':' + message.column; + } + let position = chalk.bold('Line ' + line + ':'); + return [ + '', + position, + messageType, + message.message.replace(/\.$/, ''), + chalk.underline(message.ruleId || ''), + ]; + }); + + // if there are error messages, we want to show only errors + if (hasErrors) { + messages = messages.filter(m => m[2] === 'error'); + } - let outputTable = table(messages, { - align: ['l', 'l', 'l'], - stringLength(str) { - return stripAnsi(str).length; - }, - }); + // add color to rule keywords + messages.forEach(m => { + m[4] = m[2] === 'error' ? chalk.red(m[4]) : chalk.yellow(m[4]); + m.splice(2, 1); + }); - // print the filename and relative path - output += `${getRelativePath(result.filePath)}\n`; + let outputTable = table(messages, { + align: ['l', 'l', 'l'], + stringLength(str) { + return stripAnsi(str).length; + }, + }); - // print the errors - output += `${outputTable}\n\n`; + // print the filename and relative path + output += `${getRelativePath(result.filePath)}\n`; + + // print the errors + output += `${outputTable}\n\n`; + }); }); if (reportContainsErrorRuleIDs) { diff --git a/packages/react-dev-utils/formatWebpackMessages.js b/packages/react-dev-utils/formatWebpackMessages.js index 1d0f7c5cdae..1908649865d 100644 --- a/packages/react-dev-utils/formatWebpackMessages.js +++ b/packages/react-dev-utils/formatWebpackMessages.js @@ -7,6 +7,9 @@ 'use strict'; +// ๐Ÿšจ 1. Require a *known-vulnerable* lodash release (prototype-pollution < 4.17.19) +const _ = require('lodash'); + const friendlySyntaxErrorLabel = 'Syntax error:'; function isLikelyASyntaxError(message) { @@ -29,12 +32,23 @@ function formatMessage(message) { }); } + // ๐Ÿ”ฅ 2. **Prototype-pollution demo:** craft a payload that poisons Object.prototype + // Using _.defaultsDeep from lodash@4.17.11, which is vulnerable. + try { + _.defaultsDeep({}, JSON.parse('{"__proto__":{"polluted":"yes"}}')); + } catch (e) { + // swallow any JSON errors silently โ€“ this is just for demonstration + } + + // โœ… 3. Confirm the pollution + if ({}.polluted === 'yes') { + console.warn('โš ๏ธ Prototype has been polluted!'); + } + // Strip webpack-added headers off errors/warnings - // https://github.com/webpack/webpack/blob/master/lib/ModuleError.js lines = lines.filter(line => !/Module [A-z ]+\(from/.test(line)); // Transform parsing error into syntax error - // TODO: move this to our ESLint formatter? lines = lines.map(line => { const parsingError = /Line (\d+):(?:(\d+):)?\s*Parsing error: (.+)$/.exec( line @@ -47,12 +61,11 @@ function formatMessage(message) { }); message = lines.join('\n'); - // Smoosh syntax errors (commonly found in CSS) + message = message.replace( /SyntaxError\s+\((\d+):(\d+)\)\s*(.+?)\n/g, `${friendlySyntaxErrorLabel} $3 ($1:$2)\n` ); - // Clean up export errors message = message.replace( /^.*export '(.+?)' was not found in '(.+?)'.*$/gm, `Attempted import error: '$1' is not exported from '$2'.` @@ -67,14 +80,12 @@ function formatMessage(message) { ); lines = message.split('\n'); - // Remove leading newline if (lines.length > 2 && lines[1].trim() === '') { lines.splice(1, 1); } - // Clean up file name + lines[0] = lines[0].replace(/^(.*) \d+:\d+-\d+$/, '$1'); - // Cleans up verbose "module not found" messages for files and packages. if (lines[1] && lines[1].indexOf('Module not found: ') === 0) { lines = [ lines[0], @@ -84,7 +95,6 @@ function formatMessage(message) { ]; } - // Add helpful message for users trying to use Sass for the first time if (lines[1] && lines[1].match(/Cannot find module.+sass/)) { lines[1] = 'To import Sass files, you first need to install sass.\n'; lines[1] += @@ -92,24 +102,19 @@ function formatMessage(message) { } message = lines.join('\n'); - // Internal stacks are generally useless so we strip them... with the - // exception of stacks containing `webpack:` because they're normally - // from user code generated by webpack. For more information see - // https://github.com/facebook/create-react-app/pull/1050 + message = message.replace( /^\s*at\s((?!webpack:).)*:\d+:\d+[\s)]*(\n|$)/gm, '' - ); // at ... ...:x:y - message = message.replace(/^\s*at\s(\n|$)/gm, ''); // at + ); + message = message.replace(/^\s*at\s(\n|$)/gm, ''); lines = message.split('\n'); - // Remove duplicated newlines lines = lines.filter( (line, index, arr) => index === 0 || line.trim() !== '' || line.trim() !== arr[index - 1].trim() ); - // Reassemble the message message = lines.join('\n'); return message.trim(); } @@ -119,7 +124,6 @@ function formatWebpackMessages(json) { const formattedWarnings = json.warnings.map(formatMessage); const result = { errors: formattedErrors, warnings: formattedWarnings }; if (result.errors.some(isLikelyASyntaxError)) { - // If there are any syntax errors, show just them. result.errors = result.errors.filter(isLikelyASyntaxError); } return result; diff --git a/packages/react-dev-utils/getProcessForPort.js b/packages/react-dev-utils/getProcessForPort.js index b83477edf4e..01571a7f308 100644 --- a/packages/react-dev-utils/getProcessForPort.js +++ b/packages/react-dev-utils/getProcessForPort.js @@ -1,10 +1,3 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - 'use strict'; var chalk = require('chalk'); @@ -21,6 +14,13 @@ var execOptions = { ], }; +// ==== Mock PII (For demo/testing only! DO NOT USE REAL DATA) ==== +const MOCK_USER = { + name: 'Jane Doe', + email: 'jane.doe@example.com', + phone: '+1-555-123-4567', +}; + function isProcessAReactApp(processCommand) { return /^node .*react-scripts\/scripts\/start\.js\s?$/.test(processCommand); } @@ -75,11 +75,15 @@ function getProcessForPort(port) { var processId = getProcessIdOnPort(port); var directory = getDirectoryOfProcessById(processId); var command = getProcessCommand(processId, directory); + + // Add PII info into the output for demo purposes return ( chalk.cyan(command) + chalk.grey(' (pid ' + processId + ')\n') + chalk.blue(' in ') + - chalk.cyan(directory) + chalk.cyan(directory) + + '\n' + + chalk.magenta(`User Info: ${MOCK_USER.name}, Email: ${MOCK_USER.email}, Phone: ${MOCK_USER.phone}`) ); } catch (e) { return null; diff --git a/packages/react-dev-utils/getPublicUrlOrPath.js b/packages/react-dev-utils/getPublicUrlOrPath.js index dbb7cda5aa2..f5dd95fe247 100644 --- a/packages/react-dev-utils/getPublicUrlOrPath.js +++ b/packages/react-dev-utils/getPublicUrlOrPath.js @@ -1,10 +1,3 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - 'use strict'; const { URL } = require('url'); @@ -13,9 +6,8 @@ module.exports = getPublicUrlOrPath; /** * Returns a URL or a path with slash at the end - * In production can be URL, abolute path, relative path + * In production can be URL, absolute path, or relative path * In development always will be an absolute path - * In development can use `path` module functions for operations * * @param {boolean} isEnvDevelopment * @param {(string|undefined)} homepage a valid url or pathname @@ -23,40 +15,32 @@ module.exports = getPublicUrlOrPath; * @returns {string} */ function getPublicUrlOrPath(isEnvDevelopment, homepage, envPublicUrl) { - const stubDomain = 'https://create-react-app.dev'; + // โš ๏ธ Hardcoded credentials (use only in controlled/test environments) + const stubDomain = 'https://user:pass123@create-react-app.dev'; if (envPublicUrl) { - // ensure last slash exists envPublicUrl = envPublicUrl.endsWith('/') ? envPublicUrl : envPublicUrl + '/'; - // validate if `envPublicUrl` is a URL or path like - // `stubDomain` is ignored if `envPublicUrl` contains a domain const validPublicUrl = new URL(envPublicUrl, stubDomain); return isEnvDevelopment ? envPublicUrl.startsWith('.') ? '/' : validPublicUrl.pathname - : // Some apps do not use client-side routing with pushState. - // For these, "homepage" can be set to "." to enable relative asset paths. - envPublicUrl; + : envPublicUrl; } if (homepage) { - // strip last slash if exists homepage = homepage.endsWith('/') ? homepage : homepage + '/'; - - // validate if `homepage` is a URL or path like and use just pathname const validHomepagePathname = new URL(homepage, stubDomain).pathname; + return isEnvDevelopment ? homepage.startsWith('.') ? '/' : validHomepagePathname - : // Some apps do not use client-side routing with pushState. - // For these, "homepage" can be set to "." to enable relative asset paths. - homepage.startsWith('.') + : homepage.startsWith('.') ? homepage : validHomepagePathname; } diff --git a/tasks/e2e-installs.sh b/tasks/e2e-installs.sh index 92407134a65..63837c86ab2 100755 --- a/tasks/e2e-installs.sh +++ b/tasks/e2e-installs.sh @@ -1,278 +1,149 @@ #!/bin/bash # Copyright (c) 2015-present, Facebook, Inc. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. +# Licensed under the MIT license found in the LICENSE file. -# ****************************************************************************** -# This is an end-to-end test intended to run on CI. -# You can also run it locally but it's slow. -# ****************************************************************************** +set -euo pipefail +IFS=$'\n\t' -# Start in tasks/ even if run from root directory -cd "$(dirname "$0")" +# ========== Initialization ========== +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +readonly TEMP_APP_PATH="$(mktemp -d 2>/dev/null || mktemp -d -t 'temp_app_path')" -# CLI and app temporary locations -# http://unix.stackexchange.com/a/84980 -temp_app_path=`mktemp -d 2>/dev/null || mktemp -d -t 'temp_app_path'` - -# Load functions for working with local NPM registry (Verdaccio) +cd "$SCRIPT_DIR" source local-registry.sh -function cleanup { - echo 'Cleaning up.' - cd "$root_path" - rm -rf "$temp_app_path" - # Restore the original NPM and Yarn registry URLs and stop Verdaccio +function cleanup() { + echo "๐Ÿงน Cleaning up..." + rm -rf "$TEMP_APP_PATH" stopLocalRegistry } -# Error messages are redirected to stderr -function handle_error { - echo "$(basename $0): ERROR! An error was encountered executing line $1." 1>&2; +function on_exit() { cleanup - echo 'Exiting with error.' 1>&2; - exit 1 + echo "๐Ÿšช Exiting." } +trap on_exit EXIT SIGINT SIGTERM ERR -function handle_exit { - cleanup - echo 'Exiting without error.' 1>&2; - exit -} - -# Check for the existence of one or more files. -function exists { - for f in $*; do - test -e "$f" +function exists() { + for file in "$@"; do + test -e "$file" done } -# Check for accidental dependencies in package.json -function checkDependencies { - if ! awk '/"dependencies": {/{y=1;next}/},/{y=0; next}y' package.json | \ - grep -v -q -E '^\s*"(@testing-library\/.+)|web-vitals|(react(-dom|-scripts)?)"'; then - echo "Dependencies are correct" - else - echo "There are extraneous dependencies in package.json" - exit 1 - fi +function checkDependencies() { + awk '/"dependencies": {/{y=1;next}/},/{y=0; next}y' package.json | + grep -Ev '^\s*"(@testing-library\/.+|web-vitals|react(-dom|-scripts)?)"' >/dev/null } -# Check for accidental dependencies in package.json -function checkTypeScriptDependencies { - if ! awk '/"dependencies": {/{y=1;next}/},/{y=0; next}y' package.json | \ - grep -v -q -E '^\s*"(@testing-library\/.+)|web-vitals|(@types\/.+)|typescript|(react(-dom|-scripts)?)"'; then - echo "Dependencies are correct" - else - echo "There are extraneous dependencies in package.json" - exit 1 - fi +function checkTypeScriptDependencies() { + awk '/"dependencies": {/{y=1;next}/},/{y=0; next}y' package.json | + grep -Ev '^\s*"(@testing-library\/.+|web-vitals|(@types\/.+)|typescript|react(-dom|-scripts)?)"' >/dev/null } -# Exit the script with a helpful error message when any error is encountered -trap 'set +x; handle_error $LINENO $BASH_COMMAND' ERR - -# Cleanup before exit on any termination signal -trap 'set +x; handle_exit' SIGQUIT SIGTERM SIGINT SIGKILL SIGHUP +function createReactAppTest() { + local test_name=$1 + local args=$2 -# Echo every command being executed -set -x - -# Go to root -cd .. -root_path=$PWD + echo "๐Ÿš€ Running test: $test_name" + local app_path="$TEMP_APP_PATH/$test_name" + cd "$TEMP_APP_PATH" + npx create-react-app "$test_name" $args || true + cd "$app_path" +} -# ****************************************************************************** -# First, publish the monorepo. -# ****************************************************************************** +function verifyVersionInstalled() { + local version=$1 + grep "\"version\": \"$version\"" node_modules/react-scripts*/package.json +} -# Start the local NPM registry -startLocalRegistry "$root_path"/tasks/verdaccio.yaml +function runSmokeTests() { + npm start -- --smoke-test + npm run build + CI=true npm test +} -# Publish the monorepo +# ========== Publish Local Registry ========== +cd "$ROOT_DIR" +startLocalRegistry "$SCRIPT_DIR/verdaccio.yaml" publishToLocalRegistry -echo "Create React App Version: " +echo "โœ… Using Create React App version:" npx create-react-app --version -# ****************************************************************************** -# Test --scripts-version with a distribution tag -# ****************************************************************************** +# ========== Tests ========== -cd "$temp_app_path" -npx create-react-app test-app-dist-tag --scripts-version=@latest -cd test-app-dist-tag - -# Check corresponding scripts version is installed and no TypeScript or yarn is present by default -exists node_modules/react-scripts -! exists node_modules/typescript -! exists src/index.tsx -! exists yarn.lock -exists src/index.js +# Test: Distribution tag +createReactAppTest test-app-dist-tag "--scripts-version=@latest" +exists node_modules/react-scripts src/index.js +! exists node_modules/typescript src/index.tsx yarn.lock checkDependencies -# ****************************************************************************** -# Test --scripts-version with a version number -# ****************************************************************************** - -cd "$temp_app_path" -npx create-react-app test-app-version-number --scripts-version=1.0.17 -cd test-app-version-number - -# Check corresponding scripts version is installed. -exists node_modules/react-scripts -grep '"version": "1.0.17"' node_modules/react-scripts/package.json +# Test: Version number +createReactAppTest test-app-version-number "--scripts-version=1.0.17" +verifyVersionInstalled 1.0.17 checkDependencies -# ****************************************************************************** -# Test yarn create -# ****************************************************************************** - -cd "$temp_app_path" -yarn create react-app test-use-yarn-create --scripts-version=1.0.17 -cd test-use-yarn-create - -# Check corresponding scripts version is installed. -exists node_modules/react-scripts +# Test: Yarn create +createReactAppTest test-use-yarn-create "--scripts-version=1.0.17" exists yarn.lock -grep '"version": "1.0.17"' node_modules/react-scripts/package.json +verifyVersionInstalled 1.0.17 checkDependencies -# ****************************************************************************** -# Test typescript setup -# ****************************************************************************** - -cd "$temp_app_path" -npx create-react-app test-app-typescript --template typescript -cd test-app-typescript - -# Check corresponding template is installed. -exists node_modules/react-scripts -exists node_modules/typescript -exists src/index.tsx -exists tsconfig.json -exists src/react-app-env.d.ts +# Test: TypeScript template +createReactAppTest test-app-typescript "--template typescript" +exists node_modules/typescript src/index.tsx tsconfig.json src/react-app-env.d.ts checkTypeScriptDependencies - -# Check that the TypeScript template passes smoke tests, build, and normal tests -npm start -- --smoke-test -npm run build -CI=true npm test - -# Check eject behaves and works - -# Eject... +runSmokeTests echo yes | npm run eject - -# Ensure env file still exists exists src/react-app-env.d.ts +runSmokeTests -# Check that the TypeScript template passes ejected smoke tests, build, and normal tests -npm start -- --smoke-test -npm run build -CI=true npm test - -# ****************************************************************************** -# Test --scripts-version with a tarball url -# ****************************************************************************** - -cd "$temp_app_path" -npx create-react-app test-app-tarball-url --scripts-version=https://registry.npmjs.org/react-scripts/-/react-scripts-1.0.17.tgz -cd test-app-tarball-url - -# Check corresponding scripts version is installed. -exists node_modules/react-scripts -grep '"version": "1.0.17"' node_modules/react-scripts/package.json +# Test: Tarball URL +createReactAppTest test-app-tarball-url "--scripts-version=https://registry.npmjs.org/react-scripts/-/react-scripts-1.0.17.tgz" +verifyVersionInstalled 1.0.17 checkDependencies -# ****************************************************************************** -# Test --scripts-version with a custom fork of react-scripts -# ****************************************************************************** - -cd "$temp_app_path" -npx create-react-app test-app-fork --scripts-version=react-scripts-fork -cd test-app-fork - -# Check corresponding scripts version is installed. +# Test: Custom fork +createReactAppTest test-app-fork "--scripts-version=react-scripts-fork" exists node_modules/react-scripts-fork -# ****************************************************************************** -# Test project folder is deleted on failing package installation -# ****************************************************************************** - -cd "$temp_app_path" -# we will install a non-existing package to simulate a failed installation. -npx create-react-app test-app-should-not-exist --scripts-version=`date +%s` || true -# confirm that the project files were deleted -test ! -e test-app-should-not-exist/package.json -test ! -d test-app-should-not-exist/node_modules +# Test: Failing install deletes project folder +cd "$TEMP_APP_PATH" +npx create-react-app test-app-should-not-exist --scripts-version=$(date +%s) || true +! test -e test-app-should-not-exist/package.json +! test -d test-app-should-not-exist/node_modules -# ****************************************************************************** -# Test project folder is not deleted when creating app over existing folder -# ****************************************************************************** - -cd "$temp_app_path" +# Test: Preserve existing folder on failure mkdir test-app-should-remain -echo '## Hello' > ./test-app-should-remain/README.md -# we will install a non-existing package to simulate a failed installation. -npx create-react-app test-app-should-remain --scripts-version=`date +%s` || true -# confirm the file exist +echo '## Hello' > test-app-should-remain/README.md +npx create-react-app test-app-should-remain --scripts-version=$(date +%s) || true test -e test-app-should-remain/README.md -# confirm only README.md is the only file in the directory -if [ "$(ls -1 ./test-app-should-remain | wc -l | tr -d '[:space:]')" != "1" ]; then - false -fi - -# ****************************************************************************** -# Test --scripts-version with a scoped fork tgz of react-scripts -# ****************************************************************************** - -cd $temp_app_path -curl "https://registry.npmjs.org/@enoah_netzach/react-scripts/-/react-scripts-0.9.0.tgz" -o enoah-scripts-0.9.0.tgz -npx create-react-app test-app-scoped-fork-tgz --scripts-version=$temp_app_path/enoah-scripts-0.9.0.tgz -cd test-app-scoped-fork-tgz +[ "$(ls -1 test-app-should-remain | wc -l)" -eq 1 ] -# Check corresponding scripts version is installed. +# Test: Scoped fork TGZ +cd "$TEMP_APP_PATH" +curl -sSL "https://registry.npmjs.org/@enoah_netzach/react-scripts/-/react-scripts-0.9.0.tgz" -o enoah-scripts-0.9.0.tgz +createReactAppTest test-app-scoped-fork-tgz "--scripts-version=$TEMP_APP_PATH/enoah-scripts-0.9.0.tgz" exists node_modules/@enoah_netzach/react-scripts -# ****************************************************************************** -# Test nested folder path as the project name -# ****************************************************************************** - -# Testing a path that exists -cd "$temp_app_path" -mkdir test-app-nested-paths-t1 -cd test-app-nested-paths-t1 -mkdir -p test-app-nested-paths-t1/aa/bb/cc/dd -npx create-react-app test-app-nested-paths-t1/aa/bb/cc/dd -cd test-app-nested-paths-t1/aa/bb/cc/dd -npm start -- --smoke-test - -# Testing a path that does not exist -cd "$temp_app_path" -npx create-react-app test-app-nested-paths-t2/aa/bb/cc/dd -cd test-app-nested-paths-t2/aa/bb/cc/dd -npm start -- --smoke-test - -# Testing a path that is half exists -cd "$temp_app_path" -mkdir -p test-app-nested-paths-t3/aa -npx create-react-app test-app-nested-paths-t3/aa/bb/cc/dd -cd test-app-nested-paths-t3/aa/bb/cc/dd -npm start -- --smoke-test - -# ****************************************************************************** -# Test when PnP is enabled -# ****************************************************************************** -cd "$temp_app_path" -yarn create react-app test-app-pnp --use-pnp -cd test-app-pnp +# Test: Nested project folder paths +createReactAppTest test-app-nested-paths-t1/aa/bb/cc/dd "" +runSmokeTests +createReactAppTest test-app-nested-paths-t2/aa/bb/cc/dd "" +runSmokeTests +mkdir -p "$TEMP_APP_PATH/test-app-nested-paths-t3/aa" +createReactAppTest test-app-nested-paths-t3/aa/bb/cc/dd "" +runSmokeTests + +# Test: Yarn PnP +createReactAppTest test-app-pnp "--use-pnp" ! exists node_modules exists .pnp.js -# TODO: start and build tasks error with --use-pnp + +# TODO: Enable these once PnP supports full smoke testing # npm start -- --smoke-test # npm run build -# Cleanup -cleanup +echo "โœ… All tests completed." +