You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Something about the source maps config causes problems for a simple create-react-app running in DEV mode. Column numbers are always reported as 0 (by 'source-maps') which causes the AST node matching to fail so hook names can't be located. We should really fix this, since a lot of people use create-react-app for prototyping.
Note that source maps work correctly in production builds.
Digging into this further and one thing I notice that seems unexpected is that the fileName from the parsed stack for a CRA app is something like "static/js/main.chunk.js" where as the 'source-map' "source" is e.g. "App.js".
I can create a test app (using CRA) that has a file error.js:
exportfunctiongetError(){returnnewError();}
I can import and run the getError function, run it, run the error it returns through 'error-stack-parser', and get a line and column number that are kind of arbitrary depending on the other content of my bundle...but when I parse it and run it through the 'source-maps' library, the value returned is line=2 (right) column=0 (wrong).
changed the title [-][DevTools] Improve named hooks support for create-react-app[/-][+][DevTools] Improve named hooks support for create-react-app DEV mode[/+]on Jul 13, 2021
changed the title [-][DevTools] Improve named hooks support for create-react-app DEV mode[/-][+][DevTools] Named hooks compatibility for create-react-app DEV mode[/+]on Jul 13, 2021
Below is the source code, compiled bundle, and source map. Source map is converting the useHook node (line 70, column 83 in the main.chunk.js bundle) to line 13, column 0 in source. (Line 13 is correct but column 0 is wrong.)
(this["webpackJsonptest-named-hooks"]=this["webpackJsonptest-named-hooks"]||[]).push([["main"],{/***/"./node_modules/css-loader/dist/cjs.js?!./node_modules/postcss-loader/src/index.js?!./src/index.css":
/*!**************************************************************************************************************************!*\ !*** ./node_modules/css-loader/dist/cjs.js??ref--5-oneOf-4-1!./node_modules/postcss-loader/src??postcss!./src/index.css ***! \**************************************************************************************************************************//*! exports provided: default *//***/(function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);/* harmony import */var_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(/*! ../node_modules/css-loader/dist/runtime/api.js */"./node_modules/css-loader/dist/runtime/api.js");/* harmony import */var_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default=/*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0__);// Importsvar___CSS_LOADER_EXPORT___=_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_0___default()(true);// Module___CSS_LOADER_EXPORT___.push([module.i,"body {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\n monospace;\n}\n","",{"version":3,"sources":["webpack://src/index.css"],"names":[],"mappings":"AAAA;EACE,SAAS;EACT;;cAEY;EACZ,mCAAmC;EACnC,kCAAkC;AACpC;;AAEA;EACE;aACW;AACb","sourcesContent":["body {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\n monospace;\n}\n"],"sourceRoot":""}]);// Exports/* harmony default export */__webpack_exports__["default"]=(___CSS_LOADER_EXPORT___);/***/}),/***/"./src/App.js":
/*!********************!*\ !*** ./src/App.js ***! \********************//*! exports provided: default *//***/(function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);/* WEBPACK VAR INJECTION */(function(__react_refresh_utils__,__react_refresh_error_overlay__){/* harmony export (binding) */__webpack_require__.d(__webpack_exports__,"default",function(){returnExample;});/* harmony import */varreact__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(/*! react */"./node_modules/react/index.js");/* harmony import */varreact__WEBPACK_IMPORTED_MODULE_0___default=/*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);/* harmony import */var_babel_parser__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(/*! @babel/parser */"./node_modules/@babel/parser/lib/index.js");/* harmony import */var_babel_parser__WEBPACK_IMPORTED_MODULE_1___default=/*#__PURE__*/__webpack_require__.n(_babel_parser__WEBPACK_IMPORTED_MODULE_1__);/* harmony import */varerror_stack_parser__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__(/*! error-stack-parser */"./node_modules/error-stack-parser/error-stack-parser.js");/* harmony import */varerror_stack_parser__WEBPACK_IMPORTED_MODULE_2___default=/*#__PURE__*/__webpack_require__.n(error_stack_parser__WEBPACK_IMPORTED_MODULE_2__);/* harmony import */var_babel_traverse__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__(/*! @babel/traverse */"./node_modules/@babel/traverse/lib/index.js");/* harmony import */var_babel_traverse__WEBPACK_IMPORTED_MODULE_3___default=/*#__PURE__*/__webpack_require__.n(_babel_traverse__WEBPACK_IMPORTED_MODULE_3__);/* harmony import */varsource_map__WEBPACK_IMPORTED_MODULE_4__=__webpack_require__(/*! source-map */"./node_modules/source-map/source-map.js");/* harmony import */varsource_map__WEBPACK_IMPORTED_MODULE_4___default=/*#__PURE__*/__webpack_require__.n(source_map__WEBPACK_IMPORTED_MODULE_4__);/* harmony import */var_error__WEBPACK_IMPORTED_MODULE_5__=__webpack_require__(/*! ./error */"./src/error.js");/* harmony import */varreact_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__=__webpack_require__(/*! react/jsx-dev-runtime */"./node_modules/react/jsx-dev-runtime.js");/* harmony import */varreact_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6___default=/*#__PURE__*/__webpack_require__.n(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__);__webpack_require__.$Refresh$.runtime=__webpack_require__(/*! ./node_modules/react-refresh/runtime.js */"./node_modules/react-refresh/runtime.js");__webpack_require__.$Refresh$.setup(module.i);var_jsxFileName="/Users/bvaughn/Downloads/test-named-hooks/src/App.js",_s=__webpack_require__.$Refresh$.signature();Error.prepareStackTrace=(error,trace)=>{returnerror.stack;};functionExample(){_s();const[count,setCount]=Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])(0);constdecrement=()=>setCount(count-1);constincrement=()=>setCount(count+1);return/*#__PURE__*/Object(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__["jsxDEV"])(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__["Fragment"],{children: [/*#__PURE__*/Object(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__["jsxDEV"])("button",{onClick: decrement,children: "decrement"},void0,false,{fileName: _jsxFileName,lineNumber: 20,columnNumber: 7},this),/*#__PURE__*/Object(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__["jsxDEV"])("button",{onClick: increment,children: "increment"},void0,false,{fileName: _jsxFileName,lineNumber: 21,columnNumber: 7},this),"Count: ",count]},void0,true);}_s(Example,"oDgYfYHkD9Wkv4hrAPCkI/ev3YU=");_c=Example;consterror=Object(_error__WEBPACK_IMPORTED_MODULE_5__["getError"])();constframes=error_stack_parser__WEBPACK_IMPORTED_MODULE_2___default.a.parse(error);match(frames);asyncfunctionmatch(frames){source_map__WEBPACK_IMPORTED_MODULE_4__["SourceMapConsumer"].initialize({'lib/mappings.wasm': '/mappings.wasm'});constframe=frames[0];const{
fileName,
lineNumber,
columnNumber
}=frame;constsource=awaitfetch(fileName).then(response=>response.text());constmatch=source.match(/\n\/\/#sourceMappingURL=(.+)/);constbaseURL=fileName.substr(0,fileName.lastIndexOf('/'));constsourceMapURL=`${baseURL}/${match[1]}`;constparsed=awaitfetch(sourceMapURL).then(response=>response.json());newsource_map__WEBPACK_IMPORTED_MODULE_4__["SourceMapConsumer"](parsed).then(sourceConsumer=>{constposition=sourceConsumer.originalPositionFor({line: lineNumber,column: columnNumber-1});console.log({
lineNumber,
columnNumber
},'->',position);});}var_c;__webpack_require__.$Refresh$.register(_c,"Example");constcurrentExports=__react_refresh_utils__.getModuleExports(module.i);__react_refresh_utils__.registerExportsForReactRefresh(currentExports,module.i);if(true){constisHotUpdate=!!module.hot.data;constprevExports=isHotUpdate ? module.hot.data.prevExports : null;if(__react_refresh_utils__.isReactRefreshBoundary(currentExports)){module.hot.dispose(/** * A callback to performs a full refresh if React has unrecoverable errors, * and also caches the to-be-disposed module. * @param {*} data A hot module data object from Webpack HMR. * @returns {void} */functionhotDisposeCallback(data){// We have to mutate the data object to get data registered and cacheddata.prevExports=currentExports;});module.hot.accept(/** * An error handler to allow self-recovering behaviours. * @param {Error} error An error occurred during evaluation of a module. * @returns {void} */functionhotErrorHandler(error){if(typeof__react_refresh_error_overlay__!=='undefined'&&__react_refresh_error_overlay__){__react_refresh_error_overlay__.handleRuntimeError(error);}if(typeof__react_refresh_test__!=='undefined'&&__react_refresh_test__){if(window.onHotAcceptError){window.onHotAcceptError(error.message);}}__webpack_require__.c[module.i].hot.accept(hotErrorHandler);});if(isHotUpdate){if(__react_refresh_utils__.isReactRefreshBoundary(prevExports)&&__react_refresh_utils__.shouldInvalidateReactRefreshBoundary(prevExports,currentExports)){module.hot.invalidate();}else{__react_refresh_utils__.enqueueUpdate(/** * A function to dismiss the error overlay after performing React refresh. * @returns {void} */functionupdateCallback(){if(typeof__react_refresh_error_overlay__!=='undefined'&&__react_refresh_error_overlay__){__react_refresh_error_overlay__.clearRuntimeErrors();}});}}}else{if(isHotUpdate&&__react_refresh_utils__.isReactRefreshBoundary(prevExports)){module.hot.invalidate();}}}/* WEBPACK VAR INJECTION */}.call(this,__webpack_require__(/*! ./node_modules/@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils.js */"./node_modules/@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils.js"),__webpack_require__(/*! ./node_modules/react-dev-utils/refreshOverlayInterop.js */"./node_modules/react-dev-utils/refreshOverlayInterop.js")))/***/}),/***/"./src/error.js":
/*!**********************!*\ !*** ./src/error.js ***! \**********************//*! exports provided: getError *//***/(function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);/* WEBPACK VAR INJECTION */(function(__react_refresh_utils__,__react_refresh_error_overlay__){/* harmony export (binding) */__webpack_require__.d(__webpack_exports__,"getError",function(){returngetError;});__webpack_require__.$Refresh$.runtime=__webpack_require__(/*! ./node_modules/react-refresh/runtime.js */"./node_modules/react-refresh/runtime.js");__webpack_require__.$Refresh$.setup(module.i);functiongetError(){returnnewError();}constcurrentExports=__react_refresh_utils__.getModuleExports(module.i);__react_refresh_utils__.registerExportsForReactRefresh(currentExports,module.i);if(true){constisHotUpdate=!!module.hot.data;constprevExports=isHotUpdate ? module.hot.data.prevExports : null;if(__react_refresh_utils__.isReactRefreshBoundary(currentExports)){module.hot.dispose(/** * A callback to performs a full refresh if React has unrecoverable errors, * and also caches the to-be-disposed module. * @param {*} data A hot module data object from Webpack HMR. * @returns {void} */functionhotDisposeCallback(data){// We have to mutate the data object to get data registered and cacheddata.prevExports=currentExports;});module.hot.accept(/** * An error handler to allow self-recovering behaviours. * @param {Error} error An error occurred during evaluation of a module. * @returns {void} */functionhotErrorHandler(error){if(typeof__react_refresh_error_overlay__!=='undefined'&&__react_refresh_error_overlay__){__react_refresh_error_overlay__.handleRuntimeError(error);}if(typeof__react_refresh_test__!=='undefined'&&__react_refresh_test__){if(window.onHotAcceptError){window.onHotAcceptError(error.message);}}__webpack_require__.c[module.i].hot.accept(hotErrorHandler);});if(isHotUpdate){if(__react_refresh_utils__.isReactRefreshBoundary(prevExports)&&__react_refresh_utils__.shouldInvalidateReactRefreshBoundary(prevExports,currentExports)){module.hot.invalidate();}else{__react_refresh_utils__.enqueueUpdate(/** * A function to dismiss the error overlay after performing React refresh. * @returns {void} */functionupdateCallback(){if(typeof__react_refresh_error_overlay__!=='undefined'&&__react_refresh_error_overlay__){__react_refresh_error_overlay__.clearRuntimeErrors();}});}}}else{if(isHotUpdate&&__react_refresh_utils__.isReactRefreshBoundary(prevExports)){module.hot.invalidate();}}}/* WEBPACK VAR INJECTION */}.call(this,__webpack_require__(/*! ./node_modules/@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils.js */"./node_modules/@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils.js"),__webpack_require__(/*! ./node_modules/react-dev-utils/refreshOverlayInterop.js */"./node_modules/react-dev-utils/refreshOverlayInterop.js")))/***/}),/***/"./src/index.css":
/*!***********************!*\ !*** ./src/index.css ***! \***********************//*! no static exports found *//***/(function(module,exports,__webpack_require__){varapi=__webpack_require__(/*! ../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */"./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");varcontent=__webpack_require__(/*! !../node_modules/css-loader/dist/cjs.js??ref--5-oneOf-4-1!../node_modules/postcss-loader/src??postcss!./index.css */"./node_modules/css-loader/dist/cjs.js?!./node_modules/postcss-loader/src/index.js?!./src/index.css");content=content.__esModule ? content.default : content;if(typeofcontent==='string'){content=[[module.i,content,'']];}varoptions={};options.insert="head";options.singleton=false;varupdate=api(content,options);if(true){if(!content.locals||module.hot.invalidate){varisEqualLocals=functionisEqualLocals(a,b,isNamedExport){if(!a&&b||a&&!b){returnfalse;}varp;for(pina){if(isNamedExport&&p==='default'){// eslint-disable-next-line no-continuecontinue;}if(a[p]!==b[p]){returnfalse;}}for(pinb){if(isNamedExport&&p==='default'){// eslint-disable-next-line no-continuecontinue;}if(!a[p]){returnfalse;}}returntrue;};varoldLocals=content.locals;module.hot.accept(/*! !../node_modules/css-loader/dist/cjs.js??ref--5-oneOf-4-1!../node_modules/postcss-loader/src??postcss!./index.css */"./node_modules/css-loader/dist/cjs.js?!./node_modules/postcss-loader/src/index.js?!./src/index.css",function(){content=__webpack_require__(/*! !../node_modules/css-loader/dist/cjs.js??ref--5-oneOf-4-1!../node_modules/postcss-loader/src??postcss!./index.css */"./node_modules/css-loader/dist/cjs.js?!./node_modules/postcss-loader/src/index.js?!./src/index.css");content=content.__esModule ? content.default : content;if(typeofcontent==='string'){content=[[module.i,content,'']];}if(!isEqualLocals(oldLocals,content.locals)){module.hot.invalidate();return;}oldLocals=content.locals;update(content);})}module.hot.dispose(function(){update();});}module.exports=content.locals||{};/***/}),/***/"./src/index.js":
/*!**********************!*\ !*** ./src/index.js ***! \**********************//*! no exports provided *//***/(function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);/* WEBPACK VAR INJECTION */(function(__react_refresh_utils__,__react_refresh_error_overlay__){/* harmony import */varreact__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(/*! react */"./node_modules/react/index.js");/* harmony import */varreact__WEBPACK_IMPORTED_MODULE_0___default=/*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);/* harmony import */varreact_dom__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(/*! react-dom */"./node_modules/react-dom/index.js");/* harmony import */varreact_dom__WEBPACK_IMPORTED_MODULE_1___default=/*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__);/* harmony import */var_index_css__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__(/*! ./index.css */"./src/index.css");/* harmony import */var_index_css__WEBPACK_IMPORTED_MODULE_2___default=/*#__PURE__*/__webpack_require__.n(_index_css__WEBPACK_IMPORTED_MODULE_2__);/* harmony import */var_App__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__(/*! ./App */"./src/App.js");/* harmony import */var_reportWebVitals__WEBPACK_IMPORTED_MODULE_4__=__webpack_require__(/*! ./reportWebVitals */"./src/reportWebVitals.js");/* harmony import */varreact_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__=__webpack_require__(/*! react/jsx-dev-runtime */"./node_modules/react/jsx-dev-runtime.js");/* harmony import */varreact_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5___default=/*#__PURE__*/__webpack_require__.n(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__);__webpack_require__.$Refresh$.runtime=__webpack_require__(/*! ./node_modules/react-refresh/runtime.js */"./node_modules/react-refresh/runtime.js");__webpack_require__.$Refresh$.setup(module.i);var_jsxFileName="/Users/bvaughn/Downloads/test-named-hooks/src/index.js";react_dom__WEBPACK_IMPORTED_MODULE_1___default.a.render(/*#__PURE__*/Object(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__["jsxDEV"])(react__WEBPACK_IMPORTED_MODULE_0___default.a.StrictMode,{children: /*#__PURE__*/Object(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_5__["jsxDEV"])(_App__WEBPACK_IMPORTED_MODULE_3__["default"],{},void0,false,{fileName: _jsxFileName,lineNumber: 9,columnNumber: 5},undefined)},void0,false,{fileName: _jsxFileName,lineNumber: 8,columnNumber: 3},undefined),document.getElementById('root'));// If you want to start measuring performance in your app, pass a function// to log results (for example: reportWebVitals(console.log))// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitalsObject(_reportWebVitals__WEBPACK_IMPORTED_MODULE_4__["default"])();constcurrentExports=__react_refresh_utils__.getModuleExports(module.i);__react_refresh_utils__.registerExportsForReactRefresh(currentExports,module.i);if(true){constisHotUpdate=!!module.hot.data;constprevExports=isHotUpdate ? module.hot.data.prevExports : null;if(__react_refresh_utils__.isReactRefreshBoundary(currentExports)){module.hot.dispose(/** * A callback to performs a full refresh if React has unrecoverable errors, * and also caches the to-be-disposed module. * @param {*} data A hot module data object from Webpack HMR. * @returns {void} */functionhotDisposeCallback(data){// We have to mutate the data object to get data registered and cacheddata.prevExports=currentExports;});module.hot.accept(/** * An error handler to allow self-recovering behaviours. * @param {Error} error An error occurred during evaluation of a module. * @returns {void} */functionhotErrorHandler(error){if(typeof__react_refresh_error_overlay__!=='undefined'&&__react_refresh_error_overlay__){__react_refresh_error_overlay__.handleRuntimeError(error);}if(typeof__react_refresh_test__!=='undefined'&&__react_refresh_test__){if(window.onHotAcceptError){window.onHotAcceptError(error.message);}}__webpack_require__.c[module.i].hot.accept(hotErrorHandler);});if(isHotUpdate){if(__react_refresh_utils__.isReactRefreshBoundary(prevExports)&&__react_refresh_utils__.shouldInvalidateReactRefreshBoundary(prevExports,currentExports)){module.hot.invalidate();}else{__react_refresh_utils__.enqueueUpdate(/** * A function to dismiss the error overlay after performing React refresh. * @returns {void} */functionupdateCallback(){if(typeof__react_refresh_error_overlay__!=='undefined'&&__react_refresh_error_overlay__){__react_refresh_error_overlay__.clearRuntimeErrors();}});}}}else{if(isHotUpdate&&__react_refresh_utils__.isReactRefreshBoundary(prevExports)){module.hot.invalidate();}}}/* WEBPACK VAR INJECTION */}.call(this,__webpack_require__(/*! ./node_modules/@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils.js */"./node_modules/@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils.js"),__webpack_require__(/*! ./node_modules/react-dev-utils/refreshOverlayInterop.js */"./node_modules/react-dev-utils/refreshOverlayInterop.js")))/***/}),/***/"./src/reportWebVitals.js":
/*!********************************!*\ !*** ./src/reportWebVitals.js ***! \********************************//*! exports provided: default *//***/(function(module,__webpack_exports__,__webpack_require__){"use strict";__webpack_require__.r(__webpack_exports__);/* WEBPACK VAR INJECTION */(function(__react_refresh_utils__,__react_refresh_error_overlay__){__webpack_require__.$Refresh$.runtime=__webpack_require__(/*! ./node_modules/react-refresh/runtime.js */"./node_modules/react-refresh/runtime.js");__webpack_require__.$Refresh$.setup(module.i);constreportWebVitals=onPerfEntry=>{if(onPerfEntry&&onPerfEntryinstanceofFunction){__webpack_require__.e(/*! import() */0).then(__webpack_require__.bind(null,/*! web-vitals */"./node_modules/web-vitals/dist/web-vitals.js")).then(({
getCLS,
getFID,
getFCP,
getLCP,
getTTFB
})=>{getCLS(onPerfEntry);getFID(onPerfEntry);getFCP(onPerfEntry);getLCP(onPerfEntry);getTTFB(onPerfEntry);});}};/* harmony default export */__webpack_exports__["default"]=(reportWebVitals);constcurrentExports=__react_refresh_utils__.getModuleExports(module.i);__react_refresh_utils__.registerExportsForReactRefresh(currentExports,module.i);if(true){constisHotUpdate=!!module.hot.data;constprevExports=isHotUpdate ? module.hot.data.prevExports : null;if(__react_refresh_utils__.isReactRefreshBoundary(currentExports)){module.hot.dispose(/** * A callback to performs a full refresh if React has unrecoverable errors, * and also caches the to-be-disposed module. * @param {*} data A hot module data object from Webpack HMR. * @returns {void} */functionhotDisposeCallback(data){// We have to mutate the data object to get data registered and cacheddata.prevExports=currentExports;});module.hot.accept(/** * An error handler to allow self-recovering behaviours. * @param {Error} error An error occurred during evaluation of a module. * @returns {void} */functionhotErrorHandler(error){if(typeof__react_refresh_error_overlay__!=='undefined'&&__react_refresh_error_overlay__){__react_refresh_error_overlay__.handleRuntimeError(error);}if(typeof__react_refresh_test__!=='undefined'&&__react_refresh_test__){if(window.onHotAcceptError){window.onHotAcceptError(error.message);}}__webpack_require__.c[module.i].hot.accept(hotErrorHandler);});if(isHotUpdate){if(__react_refresh_utils__.isReactRefreshBoundary(prevExports)&&__react_refresh_utils__.shouldInvalidateReactRefreshBoundary(prevExports,currentExports)){module.hot.invalidate();}else{__react_refresh_utils__.enqueueUpdate(/** * A function to dismiss the error overlay after performing React refresh. * @returns {void} */functionupdateCallback(){if(typeof__react_refresh_error_overlay__!=='undefined'&&__react_refresh_error_overlay__){__react_refresh_error_overlay__.clearRuntimeErrors();}});}}}else{if(isHotUpdate&&__react_refresh_utils__.isReactRefreshBoundary(prevExports)){module.hot.invalidate();}}}/* WEBPACK VAR INJECTION */}.call(this,__webpack_require__(/*! ./node_modules/@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils.js */"./node_modules/@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils.js"),__webpack_require__(/*! ./node_modules/react-dev-utils/refreshOverlayInterop.js */"./node_modules/react-dev-utils/refreshOverlayInterop.js")))/***/}),/***/1:
/*!**********************************************************************************************************************************************************************************************!*\ !*** multi (webpack)/hot/dev-server.js ./node_modules/@pmmmwh/react-refresh-webpack-plugin/client/ReactRefreshEntry.js ./node_modules/react-dev-utils/webpackHotDevClient.js ./src/index.js ***! \**********************************************************************************************************************************************************************************************//*! no static exports found *//***/(function(module,exports,__webpack_require__){__webpack_require__(/*! /Users/bvaughn/Downloads/test-named-hooks/node_modules/webpack/hot/dev-server.js */"./node_modules/webpack/hot/dev-server.js");__webpack_require__(/*! /Users/bvaughn/Downloads/test-named-hooks/node_modules/@pmmmwh/react-refresh-webpack-plugin/client/ReactRefreshEntry.js */"./node_modules/@pmmmwh/react-refresh-webpack-plugin/client/ReactRefreshEntry.js");__webpack_require__(/*! /Users/bvaughn/Downloads/test-named-hooks/node_modules/react-dev-utils/webpackHotDevClient.js */"./node_modules/react-dev-utils/webpackHotDevClient.js");module.exports=__webpack_require__(/*! /Users/bvaughn/Downloads/test-named-hooks/src/index.js */"./src/index.js");/***/})},[[1,"runtime-main","vendors~main"]]]);//# sourceMappingURL=main.chunk.js.map
/static/js/main.chunk.js.map
{"version":3,"file":"static/js/main.chunk.js","sources":["/Users/bvaughn/Downloads/test-named-hooks/src/index.css","/Users/bvaughn/Downloads/test-named-hooks/src/App.js","/Users/bvaughn/Downloads/test-named-hooks/src/error.js","webpack:///./src/index.css?bb0a","/Users/bvaughn/Downloads/test-named-hooks/src/index.js","/Users/bvaughn/Downloads/test-named-hooks/src/reportWebVitals.js"],"sourcesContent":["// Imports\nimport ___CSS_LOADER_API_IMPORT___ from \"../node_modules/css-loader/dist/runtime/api.js\";\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(true);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \"body {\\n margin: 0;\\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\\n sans-serif;\\n -webkit-font-smoothing: antialiased;\\n -moz-osx-font-smoothing: grayscale;\\n}\\n\\ncode {\\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\\n monospace;\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://src/index.css\"],\"names\":[],\"mappings\":\"AAAA;EACE,SAAS;EACT;;cAEY;EACZ,mCAAmC;EACnC,kCAAkC;AACpC;;AAEA;EACE;aACW;AACb\",\"sourcesContent\":[\"body {\\n margin: 0;\\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\\n sans-serif;\\n -webkit-font-smoothing: antialiased;\\n -moz-osx-font-smoothing: grayscale;\\n}\\n\\ncode {\\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\\n monospace;\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\nexport default ___CSS_LOADER_EXPORT___;\n","import React, { useState } from 'react'\nimport {parse} from '@babel/parser';\nimport ErrorStackParser from 'error-stack-parser';\nimport traverse, {NodePath, Node} from '@babel/traverse';\nimport {SourceMapConsumer} from 'source-map';\nimport {getError} from './error';\n\nError.prepareStackTrace = (error, trace) => {\n return error.stack;\n};\n\nexport default function Example() {\n const [count, setCount] = useState(0);\n\n const decrement = () => setCount(count - 1);\n const increment = () => setCount(count + 1);\n\n return (\n <>\n <button onClick={decrement}>decrement</button>\n <button onClick={increment}>increment</button>\n Count: {count}\n </>\n );\n}\n\nconst error = getError();\nconst frames = ErrorStackParser.parse(error);\n\nmatch(frames);\n\nasync function match(frames) {\n SourceMapConsumer.initialize({'lib/mappings.wasm': '/mappings.wasm'});\n\n const frame = frames[0];\n const {fileName, lineNumber, columnNumber} = frame;\n\n const source = await fetch(fileName)\n .then(response => response.text());\n\n const match = source.match(/\\n\\/\\/# sourceMappingURL=(.+)/);\n const baseURL = fileName.substr(0, fileName.lastIndexOf('/'));\n const sourceMapURL = `${baseURL}/${match[1]}`;\n\n const parsed = await fetch(sourceMapURL)\n .then(response => response.json());\n\n new SourceMapConsumer(parsed).then(sourceConsumer => {\n const position = sourceConsumer.originalPositionFor({\n line: lineNumber,\n column: columnNumber - 1 });\nconsole.log({lineNumber, columnNumber}, '->', position);\n });\n}","export function getError() {\n return new Error();\n}","var api = require(\"!../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\");\n var content = require(\"!!../node_modules/css-loader/dist/cjs.js??ref--5-oneOf-4-1!../node_modules/postcss-loader/src/index.js??postcss!./index.css\");\n\n content = content.__esModule ? content.default : content;\n\n if (typeof content === 'string') {\n content = [[module.id, content, '']];\n }\n\nvar options = {};\n\noptions.insert = \"head\";\noptions.singleton = false;\n\nvar update = api(content, options);\n\n\nif (module.hot) {\n if (!content.locals || module.hot.invalidate) {\n var isEqualLocals = function isEqualLocals(a, b, isNamedExport) {\n if (!a && b || a && !b) {\n return false;\n }\n\n var p;\n\n for (p in a) {\n if (isNamedExport && p === 'default') {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n if (a[p] !== b[p]) {\n return false;\n }\n }\n\n for (p in b) {\n if (isNamedExport && p === 'default') {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n if (!a[p]) {\n return false;\n }\n }\n\n return true;\n};\n var oldLocals = content.locals;\n\n module.hot.accept(\n \"!!../node_modules/css-loader/dist/cjs.js??ref--5-oneOf-4-1!../node_modules/postcss-loader/src/index.js??postcss!./index.css\",\n function () {\n content = require(\"!!../node_modules/css-loader/dist/cjs.js??ref--5-oneOf-4-1!../node_modules/postcss-loader/src/index.js??postcss!./index.css\");\n\n content = content.__esModule ? content.default : content;\n\n if (typeof content === 'string') {\n content = [[module.id, content, '']];\n }\n\n if (!isEqualLocals(oldLocals, content.locals)) {\n module.hot.invalidate();\n\n return;\n }\n\n oldLocals = content.locals;\n\n update(content);\n }\n )\n }\n\n module.hot.dispose(function() {\n update();\n });\n}\n\nmodule.exports = content.locals || {};","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport reportWebVitals from './reportWebVitals';\n\nReactDOM.render(\n <React.StrictMode>\n <App />\n </React.StrictMode>,\n document.getElementById('root')\n);\n\n// If you want to start measuring performance in your app, pass a function\n// to log results (for example: reportWebVitals(console.log))\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\nreportWebVitals();\n","const reportWebVitals = onPerfEntry => {\n if (onPerfEntry && onPerfEntry instanceof Function) {\n import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {\n getCLS(onPerfEntry);\n getFID(onPerfEntry);\n getFCP(onPerfEntry);\n getLCP(onPerfEntry);\n getTTFB(onPerfEntry);\n });\n }\n};\n\nexport default reportWebVitals;\n"],"mappings":";;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACNA;AACA;AACA;AACA;AACA;AACA;;;;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;AAAA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAFA;AAMA;AACA;AAdA;AACA;AADA;AAeA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAEA;AACA;AAAA;AAAA;AAAA;AAAA;AAEA;AAGA;AACA;AACA;AAEA;AAGA;AACA;AACA;AACA;AAFA;AAGA;AAAA;AAAA;AAAA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtDA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;ACjFA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AAAA;AAAA;AAAA;AADA;AAAA;AAAA;AAAA;AAAA;AAOA;AACA;AACA;AAAA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACjBA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;A","sourceRoot":""}
Activity
bvaughn commentedon Jul 13, 2021
Digging into this further and one thing I notice that seems unexpected is that the
fileName
from the parsed stack for a CRA app is something like"static/js/main.chunk.js"
where as the 'source-map' "source" is e.g."App.js"
.I can create a test app (using CRA) that has a file
error.js
:I can import and run the
getError
function, run it, run the error it returns through 'error-stack-parser', and get a line and column number that are kind of arbitrary depending on the other content of my bundle...but when I parse it and run it through the 'source-maps' library, the value returned is line=2 (right) column=0 (wrong).[-][DevTools] Improve named hooks support for create-react-app[/-][+][DevTools] Improve named hooks support for create-react-app DEV mode[/+][-][DevTools] Improve named hooks support for create-react-app DEV mode[/-][+][DevTools] Named hooks compatibility for create-react-app DEV mode[/+]bvaughn commentedon Jul 13, 2021
Below is the source code, compiled bundle, and source map. Source map is converting the
useHook
node (line 70, column 83 in themain.chunk.js
bundle) to line 13, column 0 in source. (Line 13 is correct but column 0 is wrong.)App.js
/static/js/main.chunk.js
/static/js/main.chunk.js.map
bvaughn commentedon Jul 13, 2021
It looks like one way to "fix" this is to eject create-react-app:
Then modify
config/webpack.config.js
to replace'cheap-module-source-map'
with'source-map'
(for both DEV and PROD bundles).bvaughn commentedon Jul 13, 2021
Maybe I can relax DevTools to detect column == 0 case and do line-only mapping.
It's probably not common for there to be more than one hook ID node per line anyway so maybe it's okay?
This kind of pattern would break:
But maybe DevTools could account for that by detecting if there was more than one matching node for a given line and just giving up in that case?