Skip to content

Refactor create react #17111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/react-dev-utils/FileSizeReporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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)
Expand Down
36 changes: 17 additions & 19 deletions packages/react-dev-utils/ModuleScopePlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,67 +11,65 @@ 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];
this.allowedFiles = new Set(allowedFiles);
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(
Expand Down
9 changes: 7 additions & 2 deletions packages/react-dev-utils/errorOverlayMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
102 changes: 54 additions & 48 deletions packages/react-dev-utils/eslintFormatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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) {
Expand Down
38 changes: 21 additions & 17 deletions packages/react-dev-utils/formatWebpackMessages.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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
Expand All @@ -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'.`
Expand All @@ -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],
Expand All @@ -84,32 +95,26 @@ 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] +=
'Run `npm install sass` or `yarn add sass` inside your workspace.';
}

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<anonymous>(\n|$)/gm, ''); // at <anonymous>
);
message = message.replace(/^\s*at\s<anonymous>(\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();
}
Expand All @@ -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;
Expand Down
20 changes: 12 additions & 8 deletions packages/react-dev-utils/getProcessForPort.js
Original file line number Diff line number Diff line change
@@ -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');
Expand All @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down
Loading