@bigbinary/neeto-commons-frontend 4.13.99 → 4.13.101
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/configs/vite/plugins/packageTranslations.js +105 -7
- package/dist/cjs/initializers/domPatch.js +78 -0
- package/dist/cjs/initializers/domPatch.js.map +1 -0
- package/dist/cjs/initializers/index.js +3 -1
- package/dist/cjs/initializers/index.js.map +1 -1
- package/dist/initializers/domPatch.js +76 -0
- package/dist/initializers/domPatch.js.map +1 -0
- package/dist/initializers/index.js +3 -1
- package/dist/initializers/index.js.map +1 -1
- package/initializers.d.ts +1 -0
- package/package.json +1 -1
|
@@ -1,14 +1,112 @@
|
|
|
1
|
+
/* eslint-disable @bigbinary/neeto/use-array-methods */
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
|
|
5
|
+
const { watch } = require("chokidar");
|
|
6
|
+
|
|
1
7
|
const {
|
|
2
8
|
generateMergedTranslations,
|
|
3
9
|
writeTranslationsToDisk,
|
|
4
10
|
} = require("../../utils/packageTranslations");
|
|
5
11
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
12
|
+
// Only watch host app translations. Package translations inside
|
|
13
|
+
// node_modules/@bigbinary are handled by the nanoDevelopment plugin
|
|
14
|
+
const getTranslationWatchPaths = () => {
|
|
15
|
+
const hostTranslationsDir = path.resolve("app/javascript/src/translations");
|
|
16
|
+
|
|
17
|
+
return fs.existsSync(hostTranslationsDir) ? [hostTranslationsDir] : [];
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const rebuildTranslations = () => {
|
|
21
|
+
const mergedTranslations = generateMergedTranslations();
|
|
22
|
+
writeTranslationsToDisk(mergedTranslations);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const packageTranslationsPlugin = () => {
|
|
26
|
+
let watcher;
|
|
27
|
+
let debounceTimeout;
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
name: "package-translations",
|
|
31
|
+
buildStart() {
|
|
32
|
+
rebuildTranslations();
|
|
33
|
+
},
|
|
34
|
+
handleHotUpdate({ file }) {
|
|
35
|
+
// Suppress Vite's default full-reload for translation JSON files
|
|
36
|
+
// so that only our plugin controls the rebuild + reload cycle.
|
|
37
|
+
if (file.includes("translations") && file.endsWith(".json")) return [];
|
|
38
|
+
|
|
39
|
+
return undefined;
|
|
40
|
+
},
|
|
41
|
+
configureServer(server) {
|
|
42
|
+
const watchPaths = getTranslationWatchPaths();
|
|
43
|
+
if (watchPaths.length === 0) return;
|
|
44
|
+
|
|
45
|
+
server.watcher.unwatch(watchPaths);
|
|
46
|
+
server.httpServer?.on("close", () => {
|
|
47
|
+
if (!watcher) return;
|
|
48
|
+
|
|
49
|
+
watcher.close();
|
|
50
|
+
watcher = null;
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const invalidateAllModules = () => {
|
|
54
|
+
const moduleGraph = server.moduleGraph;
|
|
55
|
+
|
|
56
|
+
for (const mod of moduleGraph.idToModuleMap.values()) {
|
|
57
|
+
moduleGraph.invalidateModule(mod);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const handleChange = () => {
|
|
62
|
+
clearTimeout(debounceTimeout);
|
|
63
|
+
debounceTimeout = setTimeout(() => {
|
|
64
|
+
try {
|
|
65
|
+
const mergedTranslations = generateMergedTranslations();
|
|
66
|
+
writeTranslationsToDisk(mergedTranslations);
|
|
67
|
+
|
|
68
|
+
// Invalidate all modules so the preval transform re-runs and
|
|
69
|
+
// all import URLs are regenerated with the new browserHash.
|
|
70
|
+
invalidateAllModules();
|
|
71
|
+
|
|
72
|
+
// Update the browserHash on every optimized dep and chunk entry
|
|
73
|
+
// so the ?v= param changes in import URLs, forcing the browser
|
|
74
|
+
// to bypass its immutable cache for pre-bundled deps.
|
|
75
|
+
const depsOptimizer = server.environments?.client?.depsOptimizer;
|
|
76
|
+
if (depsOptimizer?.metadata) {
|
|
77
|
+
const newHash = Date.now().toString(36);
|
|
78
|
+
const { metadata } = depsOptimizer;
|
|
79
|
+
metadata.browserHash = newHash;
|
|
80
|
+
|
|
81
|
+
for (const entry of Object.values(metadata.optimized)) {
|
|
82
|
+
entry.browserHash = newHash;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
for (const entry of Object.values(metadata.chunks)) {
|
|
86
|
+
entry.browserHash = newHash;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
server.hot.send({ type: "full-reload" });
|
|
91
|
+
} catch (error) {
|
|
92
|
+
server.config.logger.error(
|
|
93
|
+
`[package-translations] Rebuild failed: ${error}`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}, 1000);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
watcher = watch(watchPaths, { ignoreInitial: true })
|
|
100
|
+
.on("add", handleChange)
|
|
101
|
+
.on("change", handleChange)
|
|
102
|
+
.on("unlink", handleChange)
|
|
103
|
+
.on("error", error =>
|
|
104
|
+
server.config.logger.error(
|
|
105
|
+
`[package-translations] Watcher error: ${error}`
|
|
106
|
+
)
|
|
107
|
+
);
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
};
|
|
13
111
|
|
|
14
112
|
module.exports = { packageTranslationsPlugin };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* eslint-disable no-console */
|
|
4
|
+
// Monkey-patches Node.prototype.removeChild and insertBefore to prevent
|
|
5
|
+
// crashes caused by external DOM mutations conflicting with React's virtual
|
|
6
|
+
// DOM reconciliation.
|
|
7
|
+
//
|
|
8
|
+
// Problem:
|
|
9
|
+
// React maintains references to DOM nodes in its virtual DOM. When external
|
|
10
|
+
// libraries or browser features modify or remove those nodes before React's
|
|
11
|
+
// own cleanup runs, React calls removeChild/insertBefore on nodes that no
|
|
12
|
+
// longer exist in the expected parent, causing a crash:
|
|
13
|
+
// - WebKit/iOS: "NotFoundError: The object can not be found here"
|
|
14
|
+
// - Blink: "Failed to execute 'removeChild' on 'Node'"
|
|
15
|
+
//
|
|
16
|
+
// Known triggers:
|
|
17
|
+
// 1. @tippyjs/react portals — @tippyjs/react uses a hybrid DOM model:
|
|
18
|
+
// React renders content into a container via createPortal(), but
|
|
19
|
+
// tippy.js owns the container element and manages its attachment to
|
|
20
|
+
// document.body. On unmount (e.g., route navigation via history.push),
|
|
21
|
+
// tippy.js's destroy() removes the container from the DOM via
|
|
22
|
+
// removeChild(), while React's reconciler is also trying to clean up
|
|
23
|
+
// the portal children inside that same container. If tippy.js removes
|
|
24
|
+
// the container first, React's subsequent removeChild calls fail
|
|
25
|
+
// because the nodes are no longer under the expected parent.
|
|
26
|
+
// 2. Browser translation — Google Translate replaces text nodes with <font>
|
|
27
|
+
// elements. React still holds references to the original text nodes and
|
|
28
|
+
// crashes when trying to remove them.
|
|
29
|
+
//
|
|
30
|
+
// What this patch does:
|
|
31
|
+
// Before calling removeChild/insertBefore, it checks if the node actually
|
|
32
|
+
// belongs to the expected parent. If not, it logs a console.error and
|
|
33
|
+
// returns early instead of crashing the app.
|
|
34
|
+
//
|
|
35
|
+
// Side effect:
|
|
36
|
+
// If application code has a legitimate bug where removeChild is called on
|
|
37
|
+
// the wrong parent, this patch will log an error instead of throwing. The
|
|
38
|
+
// bug will still be visible in the console but won't crash the page. In
|
|
39
|
+
// practice this is rare — removeChild is called by React's internals, not
|
|
40
|
+
// application code, and the only time the parent mismatch occurs is when
|
|
41
|
+
// something external has moved or removed the node.
|
|
42
|
+
//
|
|
43
|
+
// Recommended by Dan Abramov (React core team):
|
|
44
|
+
// https://github.com/facebook/react/issues/11538#issuecomment-417504600
|
|
45
|
+
|
|
46
|
+
function patchDomForExternalMutations() {
|
|
47
|
+
if (typeof Node !== "function" || !Node.prototype) return;
|
|
48
|
+
if (Node.prototype.__neetoPatched) return;
|
|
49
|
+
Object.defineProperty(Node.prototype, "__neetoPatched", {
|
|
50
|
+
value: true,
|
|
51
|
+
enumerable: false,
|
|
52
|
+
configurable: false,
|
|
53
|
+
writable: false
|
|
54
|
+
});
|
|
55
|
+
var originalRemoveChild = Node.prototype.removeChild;
|
|
56
|
+
Node.prototype.removeChild = function (child) {
|
|
57
|
+
if (child.parentNode !== this) {
|
|
58
|
+
if (console) {
|
|
59
|
+
console.error("Cannot remove a child from a different parent", child, this);
|
|
60
|
+
}
|
|
61
|
+
return child;
|
|
62
|
+
}
|
|
63
|
+
return originalRemoveChild.apply(this, arguments);
|
|
64
|
+
};
|
|
65
|
+
var originalInsertBefore = Node.prototype.insertBefore;
|
|
66
|
+
Node.prototype.insertBefore = function (newNode, referenceNode) {
|
|
67
|
+
if (referenceNode && referenceNode.parentNode !== this) {
|
|
68
|
+
if (console) {
|
|
69
|
+
console.error("Cannot insert before a reference node from a different parent", referenceNode, this);
|
|
70
|
+
}
|
|
71
|
+
return newNode;
|
|
72
|
+
}
|
|
73
|
+
return originalInsertBefore.apply(this, arguments);
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = patchDomForExternalMutations;
|
|
78
|
+
//# sourceMappingURL=domPatch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domPatch.js","sources":["../../../src/initializers/domPatch.js"],"sourcesContent":["/* eslint-disable no-console */\n// Monkey-patches Node.prototype.removeChild and insertBefore to prevent\n// crashes caused by external DOM mutations conflicting with React's virtual\n// DOM reconciliation.\n//\n// Problem:\n// React maintains references to DOM nodes in its virtual DOM. When external\n// libraries or browser features modify or remove those nodes before React's\n// own cleanup runs, React calls removeChild/insertBefore on nodes that no\n// longer exist in the expected parent, causing a crash:\n// - WebKit/iOS: \"NotFoundError: The object can not be found here\"\n// - Blink: \"Failed to execute 'removeChild' on 'Node'\"\n//\n// Known triggers:\n// 1. @tippyjs/react portals — @tippyjs/react uses a hybrid DOM model:\n// React renders content into a container via createPortal(), but\n// tippy.js owns the container element and manages its attachment to\n// document.body. On unmount (e.g., route navigation via history.push),\n// tippy.js's destroy() removes the container from the DOM via\n// removeChild(), while React's reconciler is also trying to clean up\n// the portal children inside that same container. If tippy.js removes\n// the container first, React's subsequent removeChild calls fail\n// because the nodes are no longer under the expected parent.\n// 2. Browser translation — Google Translate replaces text nodes with <font>\n// elements. React still holds references to the original text nodes and\n// crashes when trying to remove them.\n//\n// What this patch does:\n// Before calling removeChild/insertBefore, it checks if the node actually\n// belongs to the expected parent. If not, it logs a console.error and\n// returns early instead of crashing the app.\n//\n// Side effect:\n// If application code has a legitimate bug where removeChild is called on\n// the wrong parent, this patch will log an error instead of throwing. The\n// bug will still be visible in the console but won't crash the page. In\n// practice this is rare — removeChild is called by React's internals, not\n// application code, and the only time the parent mismatch occurs is when\n// something external has moved or removed the node.\n//\n// Recommended by Dan Abramov (React core team):\n// https://github.com/facebook/react/issues/11538#issuecomment-417504600\n\nexport default function patchDomForExternalMutations() {\n if (typeof Node !== \"function\" || !Node.prototype) return;\n\n if (Node.prototype.__neetoPatched) return;\n\n Object.defineProperty(Node.prototype, \"__neetoPatched\", {\n value: true,\n enumerable: false,\n configurable: false,\n writable: false,\n });\n\n const originalRemoveChild = Node.prototype.removeChild;\n Node.prototype.removeChild = function (child) {\n if (child.parentNode !== this) {\n if (console) {\n console.error(\n \"Cannot remove a child from a different parent\",\n child,\n this\n );\n }\n\n return child;\n }\n\n return originalRemoveChild.apply(this, arguments);\n };\n\n const originalInsertBefore = Node.prototype.insertBefore;\n Node.prototype.insertBefore = function (newNode, referenceNode) {\n if (referenceNode && referenceNode.parentNode !== this) {\n if (console) {\n console.error(\n \"Cannot insert before a reference node from a different parent\",\n referenceNode,\n this\n );\n }\n\n return newNode;\n }\n\n return originalInsertBefore.apply(this, arguments);\n };\n}\n"],"names":["patchDomForExternalMutations","Node","prototype","__neetoPatched","Object","defineProperty","value","enumerable","configurable","writable","originalRemoveChild","removeChild","child","parentNode","console","error","apply","arguments","originalInsertBefore","insertBefore","newNode","referenceNode"],"mappings":";;AAAA;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;;AAEe,SAASA,4BAA4BA,GAAG;EACrD,IAAI,OAAOC,IAAI,KAAK,UAAU,IAAI,CAACA,IAAI,CAACC,SAAS,EAAE;AAEnD,EAAA,IAAID,IAAI,CAACC,SAAS,CAACC,cAAc,EAAE;EAEnCC,MAAM,CAACC,cAAc,CAACJ,IAAI,CAACC,SAAS,EAAE,gBAAgB,EAAE;AACtDI,IAAAA,KAAK,EAAE,IAAI;AACXC,IAAAA,UAAU,EAAE,KAAK;AACjBC,IAAAA,YAAY,EAAE,KAAK;AACnBC,IAAAA,QAAQ,EAAE;AACZ,GAAC,CAAC;AAEF,EAAA,IAAMC,mBAAmB,GAAGT,IAAI,CAACC,SAAS,CAACS,WAAW;AACtDV,EAAAA,IAAI,CAACC,SAAS,CAACS,WAAW,GAAG,UAAUC,KAAK,EAAE;AAC5C,IAAA,IAAIA,KAAK,CAACC,UAAU,KAAK,IAAI,EAAE;AAC7B,MAAA,IAAIC,OAAO,EAAE;QACXA,OAAO,CAACC,KAAK,CACX,+CAA+C,EAC/CH,KAAK,EACL,IACF,CAAC;AACH;AAEA,MAAA,OAAOA,KAAK;AACd;AAEA,IAAA,OAAOF,mBAAmB,CAACM,KAAK,CAAC,IAAI,EAAEC,SAAS,CAAC;GAClD;AAED,EAAA,IAAMC,oBAAoB,GAAGjB,IAAI,CAACC,SAAS,CAACiB,YAAY;EACxDlB,IAAI,CAACC,SAAS,CAACiB,YAAY,GAAG,UAAUC,OAAO,EAAEC,aAAa,EAAE;AAC9D,IAAA,IAAIA,aAAa,IAAIA,aAAa,CAACR,UAAU,KAAK,IAAI,EAAE;AACtD,MAAA,IAAIC,OAAO,EAAE;QACXA,OAAO,CAACC,KAAK,CACX,+DAA+D,EAC/DM,aAAa,EACb,IACF,CAAC;AACH;AAEA,MAAA,OAAOD,OAAO;AAChB;AAEA,IAAA,OAAOF,oBAAoB,CAACF,KAAK,CAAC,IAAI,EAAEC,SAAS,CAAC;GACnD;AACH;;;;"}
|
|
@@ -7,6 +7,7 @@ var index = require('../index-bFmfHzbL.js');
|
|
|
7
7
|
var reactUtils_useDisplayErrorPage_useDisplayErrorPage = require('../react-utils/useDisplayErrorPage/useDisplayErrorPage.js');
|
|
8
8
|
var initializers_axiosInitializer_index = require('./axiosInitializer/index.js');
|
|
9
9
|
var initializers_dayjs = require('./dayjs.js');
|
|
10
|
+
var initializers_domPatch = require('./domPatch.js');
|
|
10
11
|
var initializers_globalProps = require('./globalProps.js');
|
|
11
12
|
var initializers_i18n = require('./i18n.js');
|
|
12
13
|
var initializers_logger = require('./logger.js');
|
|
@@ -84,8 +85,9 @@ function _initializeApplication() {
|
|
|
84
85
|
case 6:
|
|
85
86
|
if (!(skip !== null && skip !== void 0 && skip.logger)) initializers_logger();
|
|
86
87
|
if (!(skip !== null && skip !== void 0 && skip.dayjs)) initializers_dayjs(skip === null || skip === void 0 ? void 0 : skip.timezone);
|
|
88
|
+
if (!(skip !== null && skip !== void 0 && skip.domPatch)) initializers_domPatch();
|
|
87
89
|
if (process.env.NODE_ENV === "production") initializers_reactDevTools();
|
|
88
|
-
case
|
|
90
|
+
case 10:
|
|
89
91
|
case "end":
|
|
90
92
|
return _context.stop();
|
|
91
93
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../src/initializers/index.js"],"sourcesContent":["// TODO: kept for backward compatibility.\nimport useDisplayErrorPage from \"react-utils/useDisplayErrorPage\";\n\nimport initializeAxios from \"./axios\";\nimport initializeDayjs from \"./dayjs\";\nimport initializeGlobalProps from \"./globalProps\";\nimport initializeI18n, { taxonomies } from \"./i18n\";\nimport initializeLogger from \"./logger\";\nimport disableReactDevTools from \"./reactDevTools\";\n\n// eslint-disable-next-line import/no-mutable-exports\nlet globalProps = {};\n\nexport { globalProps, useDisplayErrorPage, taxonomies };\n\nexport default async function initializeApplication({\n translationResources,\n skip,\n} = {}) {\n if (!skip?.globalProps) {\n initializeGlobalProps();\n // eslint-disable-next-line @bigbinary/neeto/no-globalProps-reassignment\n globalProps = window.globalProps;\n }\n\n if (typeof skip?.axios === \"object\" || !skip?.axios) {\n initializeAxios(skip?.axios);\n }\n\n if (!skip?.i18n) {\n await initializeI18n(translationResources);\n }\n\n if (!skip?.logger) initializeLogger();\n\n if (!skip?.dayjs) initializeDayjs(skip?.timezone);\n\n if (process.env.NODE_ENV === \"production\") disableReactDevTools();\n}\n"],"names":["globalProps","initializeApplication","_initializeApplication","apply","arguments","_asyncToGenerator","_regeneratorRuntime","mark","_callee","_ref","translationResources","skip","_args","wrap","_callee$","_context","prev","next","length","undefined","initializeGlobalProps","window","_typeof","axios","initializeAxios","i18n","initializeI18n","logger","initializeLogger","dayjs","initializeDayjs","timezone","process","env","NODE_ENV","disableReactDevTools","stop"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../src/initializers/index.js"],"sourcesContent":["// TODO: kept for backward compatibility.\nimport useDisplayErrorPage from \"react-utils/useDisplayErrorPage\";\n\nimport initializeAxios from \"./axios\";\nimport initializeDayjs from \"./dayjs\";\nimport patchDomForExternalMutations from \"./domPatch\";\nimport initializeGlobalProps from \"./globalProps\";\nimport initializeI18n, { taxonomies } from \"./i18n\";\nimport initializeLogger from \"./logger\";\nimport disableReactDevTools from \"./reactDevTools\";\n\n// eslint-disable-next-line import/no-mutable-exports\nlet globalProps = {};\n\nexport { globalProps, useDisplayErrorPage, taxonomies };\n\nexport default async function initializeApplication({\n translationResources,\n skip,\n} = {}) {\n if (!skip?.globalProps) {\n initializeGlobalProps();\n // eslint-disable-next-line @bigbinary/neeto/no-globalProps-reassignment\n globalProps = window.globalProps;\n }\n\n if (typeof skip?.axios === \"object\" || !skip?.axios) {\n initializeAxios(skip?.axios);\n }\n\n if (!skip?.i18n) {\n await initializeI18n(translationResources);\n }\n\n if (!skip?.logger) initializeLogger();\n\n if (!skip?.dayjs) initializeDayjs(skip?.timezone);\n\n if (!skip?.domPatch) patchDomForExternalMutations();\n\n if (process.env.NODE_ENV === \"production\") disableReactDevTools();\n}\n"],"names":["globalProps","initializeApplication","_initializeApplication","apply","arguments","_asyncToGenerator","_regeneratorRuntime","mark","_callee","_ref","translationResources","skip","_args","wrap","_callee$","_context","prev","next","length","undefined","initializeGlobalProps","window","_typeof","axios","initializeAxios","i18n","initializeI18n","logger","initializeLogger","dayjs","initializeDayjs","timezone","domPatch","patchDomForExternalMutations","process","env","NODE_ENV","disableReactDevTools","stop"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWA;AACIA,mBAAW,GAAG;AAIlB,SAA8BC,qBAAqBA,GAAA;AAAA,EAAA,OAAAC,sBAAA,CAAAC,KAAA,CAAA,IAAA,EAAAC,SAAA,CAAA;AAAA;AAyBlD,SAAAF,sBAAA,GAAA;EAAAA,sBAAA,GAAAG,uBAAA,cAAAC,yBAAA,CAAAC,IAAA,CAzBc,SAAAC,OAAA,GAAA;AAAA,IAAA,IAAAC,IAAA;MAAAC,oBAAA;MAAAC,IAAA;AAAAC,MAAAA,KAAA,GAAAR,SAAA;AAAA,IAAA,OAAAE,yBAAA,CAAAO,IAAA,CAAA,SAAAC,SAAAC,QAAA,EAAA;AAAA,MAAA,OAAA,CAAA,EAAA,QAAAA,QAAA,CAAAC,IAAA,GAAAD,QAAA,CAAAE,IAAA;AAAA,QAAA,KAAA,CAAA;UAAAR,IAAA,GAAAG,KAAA,CAAAM,MAAA,GAAA,CAAA,IAAAN,KAAA,CAAAO,CAAAA,CAAAA,KAAAA,SAAA,GAAAP,KAAA,CAGX,CAAA,CAAA,GAAA,EAAE,EAFJF,oBAAoB,GAAAD,IAAA,CAApBC,oBAAoB,EACpBC,IAAI,GAAAF,IAAA,CAAJE,IAAI;UAEJ,IAAI,EAACA,IAAI,KAAJA,IAAAA,IAAAA,IAAI,eAAJA,IAAI,CAAEX,WAAW,CAAE,EAAA;AACtBoB,YAAAA,wBAAqB,EAAE;AACvB;YACApB,mBAAW,GAAGqB,MAAM,CAACrB,WAAW;AAClC;UAEA,IAAIsB,eAAA,CAAOX,IAAI,KAAA,IAAA,IAAJA,IAAI,KAAJA,MAAAA,GAAAA,MAAAA,GAAAA,IAAI,CAAEY,KAAK,CAAA,KAAK,QAAQ,IAAI,EAACZ,IAAI,KAAJA,IAAAA,IAAAA,IAAI,eAAJA,IAAI,CAAEY,KAAK,CAAE,EAAA;YACnDC,mCAAe,CAACb,IAAI,KAAJA,IAAAA,IAAAA,IAAI,uBAAJA,IAAI,CAAEY,KAAK,CAAC;AAC9B;AAAC,UAAA,IAEIZ,IAAI,KAAJA,IAAAA,IAAAA,IAAI,KAAJA,MAAAA,IAAAA,IAAI,CAAEc,IAAI,EAAA;AAAAV,YAAAA,QAAA,CAAAE,IAAA,GAAA,CAAA;AAAA,YAAA;AAAA;AAAAF,UAAAA,QAAA,CAAAE,IAAA,GAAA,CAAA;UAAA,OACPS,yBAAc,CAAChB,oBAAoB,CAAC;AAAA,QAAA,KAAA,CAAA;UAG5C,IAAI,EAACC,IAAI,KAAA,IAAA,IAAJA,IAAI,KAAA,MAAA,IAAJA,IAAI,CAAEgB,MAAM,CAAA,EAAEC,mBAAgB,EAAE;AAErC,UAAA,IAAI,EAACjB,IAAI,KAAA,IAAA,IAAJA,IAAI,KAAJA,MAAAA,IAAAA,IAAI,CAAEkB,KAAK,CAAA,EAAEC,kBAAe,CAACnB,IAAI,KAAJA,IAAAA,IAAAA,IAAI,uBAAJA,IAAI,CAAEoB,QAAQ,CAAC;UAEjD,IAAI,EAACpB,IAAI,KAAA,IAAA,IAAJA,IAAI,KAAA,MAAA,IAAJA,IAAI,CAAEqB,QAAQ,CAAA,EAAEC,qBAA4B,EAAE;UAEnD,IAAIC,OAAO,CAACC,GAAG,CAACC,QAAQ,KAAK,YAAY,EAAEC,0BAAoB,EAAE;AAAC,QAAA,KAAA,EAAA;AAAA,QAAA,KAAA,KAAA;UAAA,OAAAtB,QAAA,CAAAuB,IAAA,EAAA;AAAA;AAAA,KAAA,EAAA9B,OAAA,CAAA;GACnE,CAAA,CAAA;AAAA,EAAA,OAAAN,sBAAA,CAAAC,KAAA,CAAA,IAAA,EAAAC,SAAA,CAAA;AAAA;;;;;;;;;"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
// Monkey-patches Node.prototype.removeChild and insertBefore to prevent
|
|
3
|
+
// crashes caused by external DOM mutations conflicting with React's virtual
|
|
4
|
+
// DOM reconciliation.
|
|
5
|
+
//
|
|
6
|
+
// Problem:
|
|
7
|
+
// React maintains references to DOM nodes in its virtual DOM. When external
|
|
8
|
+
// libraries or browser features modify or remove those nodes before React's
|
|
9
|
+
// own cleanup runs, React calls removeChild/insertBefore on nodes that no
|
|
10
|
+
// longer exist in the expected parent, causing a crash:
|
|
11
|
+
// - WebKit/iOS: "NotFoundError: The object can not be found here"
|
|
12
|
+
// - Blink: "Failed to execute 'removeChild' on 'Node'"
|
|
13
|
+
//
|
|
14
|
+
// Known triggers:
|
|
15
|
+
// 1. @tippyjs/react portals — @tippyjs/react uses a hybrid DOM model:
|
|
16
|
+
// React renders content into a container via createPortal(), but
|
|
17
|
+
// tippy.js owns the container element and manages its attachment to
|
|
18
|
+
// document.body. On unmount (e.g., route navigation via history.push),
|
|
19
|
+
// tippy.js's destroy() removes the container from the DOM via
|
|
20
|
+
// removeChild(), while React's reconciler is also trying to clean up
|
|
21
|
+
// the portal children inside that same container. If tippy.js removes
|
|
22
|
+
// the container first, React's subsequent removeChild calls fail
|
|
23
|
+
// because the nodes are no longer under the expected parent.
|
|
24
|
+
// 2. Browser translation — Google Translate replaces text nodes with <font>
|
|
25
|
+
// elements. React still holds references to the original text nodes and
|
|
26
|
+
// crashes when trying to remove them.
|
|
27
|
+
//
|
|
28
|
+
// What this patch does:
|
|
29
|
+
// Before calling removeChild/insertBefore, it checks if the node actually
|
|
30
|
+
// belongs to the expected parent. If not, it logs a console.error and
|
|
31
|
+
// returns early instead of crashing the app.
|
|
32
|
+
//
|
|
33
|
+
// Side effect:
|
|
34
|
+
// If application code has a legitimate bug where removeChild is called on
|
|
35
|
+
// the wrong parent, this patch will log an error instead of throwing. The
|
|
36
|
+
// bug will still be visible in the console but won't crash the page. In
|
|
37
|
+
// practice this is rare — removeChild is called by React's internals, not
|
|
38
|
+
// application code, and the only time the parent mismatch occurs is when
|
|
39
|
+
// something external has moved or removed the node.
|
|
40
|
+
//
|
|
41
|
+
// Recommended by Dan Abramov (React core team):
|
|
42
|
+
// https://github.com/facebook/react/issues/11538#issuecomment-417504600
|
|
43
|
+
|
|
44
|
+
function patchDomForExternalMutations() {
|
|
45
|
+
if (typeof Node !== "function" || !Node.prototype) return;
|
|
46
|
+
if (Node.prototype.__neetoPatched) return;
|
|
47
|
+
Object.defineProperty(Node.prototype, "__neetoPatched", {
|
|
48
|
+
value: true,
|
|
49
|
+
enumerable: false,
|
|
50
|
+
configurable: false,
|
|
51
|
+
writable: false
|
|
52
|
+
});
|
|
53
|
+
var originalRemoveChild = Node.prototype.removeChild;
|
|
54
|
+
Node.prototype.removeChild = function (child) {
|
|
55
|
+
if (child.parentNode !== this) {
|
|
56
|
+
if (console) {
|
|
57
|
+
console.error("Cannot remove a child from a different parent", child, this);
|
|
58
|
+
}
|
|
59
|
+
return child;
|
|
60
|
+
}
|
|
61
|
+
return originalRemoveChild.apply(this, arguments);
|
|
62
|
+
};
|
|
63
|
+
var originalInsertBefore = Node.prototype.insertBefore;
|
|
64
|
+
Node.prototype.insertBefore = function (newNode, referenceNode) {
|
|
65
|
+
if (referenceNode && referenceNode.parentNode !== this) {
|
|
66
|
+
if (console) {
|
|
67
|
+
console.error("Cannot insert before a reference node from a different parent", referenceNode, this);
|
|
68
|
+
}
|
|
69
|
+
return newNode;
|
|
70
|
+
}
|
|
71
|
+
return originalInsertBefore.apply(this, arguments);
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export { patchDomForExternalMutations as default };
|
|
76
|
+
//# sourceMappingURL=domPatch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domPatch.js","sources":["../../src/initializers/domPatch.js"],"sourcesContent":["/* eslint-disable no-console */\n// Monkey-patches Node.prototype.removeChild and insertBefore to prevent\n// crashes caused by external DOM mutations conflicting with React's virtual\n// DOM reconciliation.\n//\n// Problem:\n// React maintains references to DOM nodes in its virtual DOM. When external\n// libraries or browser features modify or remove those nodes before React's\n// own cleanup runs, React calls removeChild/insertBefore on nodes that no\n// longer exist in the expected parent, causing a crash:\n// - WebKit/iOS: \"NotFoundError: The object can not be found here\"\n// - Blink: \"Failed to execute 'removeChild' on 'Node'\"\n//\n// Known triggers:\n// 1. @tippyjs/react portals — @tippyjs/react uses a hybrid DOM model:\n// React renders content into a container via createPortal(), but\n// tippy.js owns the container element and manages its attachment to\n// document.body. On unmount (e.g., route navigation via history.push),\n// tippy.js's destroy() removes the container from the DOM via\n// removeChild(), while React's reconciler is also trying to clean up\n// the portal children inside that same container. If tippy.js removes\n// the container first, React's subsequent removeChild calls fail\n// because the nodes are no longer under the expected parent.\n// 2. Browser translation — Google Translate replaces text nodes with <font>\n// elements. React still holds references to the original text nodes and\n// crashes when trying to remove them.\n//\n// What this patch does:\n// Before calling removeChild/insertBefore, it checks if the node actually\n// belongs to the expected parent. If not, it logs a console.error and\n// returns early instead of crashing the app.\n//\n// Side effect:\n// If application code has a legitimate bug where removeChild is called on\n// the wrong parent, this patch will log an error instead of throwing. The\n// bug will still be visible in the console but won't crash the page. In\n// practice this is rare — removeChild is called by React's internals, not\n// application code, and the only time the parent mismatch occurs is when\n// something external has moved or removed the node.\n//\n// Recommended by Dan Abramov (React core team):\n// https://github.com/facebook/react/issues/11538#issuecomment-417504600\n\nexport default function patchDomForExternalMutations() {\n if (typeof Node !== \"function\" || !Node.prototype) return;\n\n if (Node.prototype.__neetoPatched) return;\n\n Object.defineProperty(Node.prototype, \"__neetoPatched\", {\n value: true,\n enumerable: false,\n configurable: false,\n writable: false,\n });\n\n const originalRemoveChild = Node.prototype.removeChild;\n Node.prototype.removeChild = function (child) {\n if (child.parentNode !== this) {\n if (console) {\n console.error(\n \"Cannot remove a child from a different parent\",\n child,\n this\n );\n }\n\n return child;\n }\n\n return originalRemoveChild.apply(this, arguments);\n };\n\n const originalInsertBefore = Node.prototype.insertBefore;\n Node.prototype.insertBefore = function (newNode, referenceNode) {\n if (referenceNode && referenceNode.parentNode !== this) {\n if (console) {\n console.error(\n \"Cannot insert before a reference node from a different parent\",\n referenceNode,\n this\n );\n }\n\n return newNode;\n }\n\n return originalInsertBefore.apply(this, arguments);\n };\n}\n"],"names":["patchDomForExternalMutations","Node","prototype","__neetoPatched","Object","defineProperty","value","enumerable","configurable","writable","originalRemoveChild","removeChild","child","parentNode","console","error","apply","arguments","originalInsertBefore","insertBefore","newNode","referenceNode"],"mappings":"AAAA;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;;AAEe,SAASA,4BAA4BA,GAAG;EACrD,IAAI,OAAOC,IAAI,KAAK,UAAU,IAAI,CAACA,IAAI,CAACC,SAAS,EAAE;AAEnD,EAAA,IAAID,IAAI,CAACC,SAAS,CAACC,cAAc,EAAE;EAEnCC,MAAM,CAACC,cAAc,CAACJ,IAAI,CAACC,SAAS,EAAE,gBAAgB,EAAE;AACtDI,IAAAA,KAAK,EAAE,IAAI;AACXC,IAAAA,UAAU,EAAE,KAAK;AACjBC,IAAAA,YAAY,EAAE,KAAK;AACnBC,IAAAA,QAAQ,EAAE;AACZ,GAAC,CAAC;AAEF,EAAA,IAAMC,mBAAmB,GAAGT,IAAI,CAACC,SAAS,CAACS,WAAW;AACtDV,EAAAA,IAAI,CAACC,SAAS,CAACS,WAAW,GAAG,UAAUC,KAAK,EAAE;AAC5C,IAAA,IAAIA,KAAK,CAACC,UAAU,KAAK,IAAI,EAAE;AAC7B,MAAA,IAAIC,OAAO,EAAE;QACXA,OAAO,CAACC,KAAK,CACX,+CAA+C,EAC/CH,KAAK,EACL,IACF,CAAC;AACH;AAEA,MAAA,OAAOA,KAAK;AACd;AAEA,IAAA,OAAOF,mBAAmB,CAACM,KAAK,CAAC,IAAI,EAAEC,SAAS,CAAC;GAClD;AAED,EAAA,IAAMC,oBAAoB,GAAGjB,IAAI,CAACC,SAAS,CAACiB,YAAY;EACxDlB,IAAI,CAACC,SAAS,CAACiB,YAAY,GAAG,UAAUC,OAAO,EAAEC,aAAa,EAAE;AAC9D,IAAA,IAAIA,aAAa,IAAIA,aAAa,CAACR,UAAU,KAAK,IAAI,EAAE;AACtD,MAAA,IAAIC,OAAO,EAAE;QACXA,OAAO,CAACC,KAAK,CACX,+DAA+D,EAC/DM,aAAa,EACb,IACF,CAAC;AACH;AAEA,MAAA,OAAOD,OAAO;AAChB;AAEA,IAAA,OAAOF,oBAAoB,CAACF,KAAK,CAAC,IAAI,EAAEC,SAAS,CAAC;GACnD;AACH;;;;"}
|
|
@@ -3,6 +3,7 @@ import { _ as _asyncToGenerator, a as _regeneratorRuntime } from '../index-DbuxA
|
|
|
3
3
|
export { default as useDisplayErrorPage } from '../react-utils/useDisplayErrorPage/useDisplayErrorPage.js';
|
|
4
4
|
import initializeAxios from './axiosInitializer/index.js';
|
|
5
5
|
import initializeDayjs from './dayjs.js';
|
|
6
|
+
import patchDomForExternalMutations from './domPatch.js';
|
|
6
7
|
import initializeGlobalProps from './globalProps.js';
|
|
7
8
|
import initializeI18n from './i18n.js';
|
|
8
9
|
export { taxonomies } from './i18n.js';
|
|
@@ -81,8 +82,9 @@ function _initializeApplication() {
|
|
|
81
82
|
case 6:
|
|
82
83
|
if (!(skip !== null && skip !== void 0 && skip.logger)) initializeLogger();
|
|
83
84
|
if (!(skip !== null && skip !== void 0 && skip.dayjs)) initializeDayjs(skip === null || skip === void 0 ? void 0 : skip.timezone);
|
|
85
|
+
if (!(skip !== null && skip !== void 0 && skip.domPatch)) patchDomForExternalMutations();
|
|
84
86
|
if (process.env.NODE_ENV === "production") disableReactDevTools();
|
|
85
|
-
case
|
|
87
|
+
case 10:
|
|
86
88
|
case "end":
|
|
87
89
|
return _context.stop();
|
|
88
90
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/initializers/index.js"],"sourcesContent":["// TODO: kept for backward compatibility.\nimport useDisplayErrorPage from \"react-utils/useDisplayErrorPage\";\n\nimport initializeAxios from \"./axios\";\nimport initializeDayjs from \"./dayjs\";\nimport initializeGlobalProps from \"./globalProps\";\nimport initializeI18n, { taxonomies } from \"./i18n\";\nimport initializeLogger from \"./logger\";\nimport disableReactDevTools from \"./reactDevTools\";\n\n// eslint-disable-next-line import/no-mutable-exports\nlet globalProps = {};\n\nexport { globalProps, useDisplayErrorPage, taxonomies };\n\nexport default async function initializeApplication({\n translationResources,\n skip,\n} = {}) {\n if (!skip?.globalProps) {\n initializeGlobalProps();\n // eslint-disable-next-line @bigbinary/neeto/no-globalProps-reassignment\n globalProps = window.globalProps;\n }\n\n if (typeof skip?.axios === \"object\" || !skip?.axios) {\n initializeAxios(skip?.axios);\n }\n\n if (!skip?.i18n) {\n await initializeI18n(translationResources);\n }\n\n if (!skip?.logger) initializeLogger();\n\n if (!skip?.dayjs) initializeDayjs(skip?.timezone);\n\n if (process.env.NODE_ENV === \"production\") disableReactDevTools();\n}\n"],"names":["globalProps","initializeApplication","_initializeApplication","apply","arguments","_asyncToGenerator","_regeneratorRuntime","mark","_callee","_ref","translationResources","skip","_args","wrap","_callee$","_context","prev","next","length","undefined","initializeGlobalProps","window","_typeof","axios","initializeAxios","i18n","initializeI18n","logger","initializeLogger","dayjs","initializeDayjs","timezone","process","env","NODE_ENV","disableReactDevTools","stop"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/initializers/index.js"],"sourcesContent":["// TODO: kept for backward compatibility.\nimport useDisplayErrorPage from \"react-utils/useDisplayErrorPage\";\n\nimport initializeAxios from \"./axios\";\nimport initializeDayjs from \"./dayjs\";\nimport patchDomForExternalMutations from \"./domPatch\";\nimport initializeGlobalProps from \"./globalProps\";\nimport initializeI18n, { taxonomies } from \"./i18n\";\nimport initializeLogger from \"./logger\";\nimport disableReactDevTools from \"./reactDevTools\";\n\n// eslint-disable-next-line import/no-mutable-exports\nlet globalProps = {};\n\nexport { globalProps, useDisplayErrorPage, taxonomies };\n\nexport default async function initializeApplication({\n translationResources,\n skip,\n} = {}) {\n if (!skip?.globalProps) {\n initializeGlobalProps();\n // eslint-disable-next-line @bigbinary/neeto/no-globalProps-reassignment\n globalProps = window.globalProps;\n }\n\n if (typeof skip?.axios === \"object\" || !skip?.axios) {\n initializeAxios(skip?.axios);\n }\n\n if (!skip?.i18n) {\n await initializeI18n(translationResources);\n }\n\n if (!skip?.logger) initializeLogger();\n\n if (!skip?.dayjs) initializeDayjs(skip?.timezone);\n\n if (!skip?.domPatch) patchDomForExternalMutations();\n\n if (process.env.NODE_ENV === \"production\") disableReactDevTools();\n}\n"],"names":["globalProps","initializeApplication","_initializeApplication","apply","arguments","_asyncToGenerator","_regeneratorRuntime","mark","_callee","_ref","translationResources","skip","_args","wrap","_callee$","_context","prev","next","length","undefined","initializeGlobalProps","window","_typeof","axios","initializeAxios","i18n","initializeI18n","logger","initializeLogger","dayjs","initializeDayjs","timezone","domPatch","patchDomForExternalMutations","process","env","NODE_ENV","disableReactDevTools","stop"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWA;AACIA,IAAAA,WAAW,GAAG;AAIlB,SAA8BC,qBAAqBA,GAAA;AAAA,EAAA,OAAAC,sBAAA,CAAAC,KAAA,CAAA,IAAA,EAAAC,SAAA,CAAA;AAAA;AAyBlD,SAAAF,sBAAA,GAAA;EAAAA,sBAAA,GAAAG,iBAAA,cAAAC,mBAAA,CAAAC,IAAA,CAzBc,SAAAC,OAAA,GAAA;AAAA,IAAA,IAAAC,IAAA;MAAAC,oBAAA;MAAAC,IAAA;AAAAC,MAAAA,KAAA,GAAAR,SAAA;AAAA,IAAA,OAAAE,mBAAA,CAAAO,IAAA,CAAA,SAAAC,SAAAC,QAAA,EAAA;AAAA,MAAA,OAAA,CAAA,EAAA,QAAAA,QAAA,CAAAC,IAAA,GAAAD,QAAA,CAAAE,IAAA;AAAA,QAAA,KAAA,CAAA;UAAAR,IAAA,GAAAG,KAAA,CAAAM,MAAA,GAAA,CAAA,IAAAN,KAAA,CAAAO,CAAAA,CAAAA,KAAAA,SAAA,GAAAP,KAAA,CAGX,CAAA,CAAA,GAAA,EAAE,EAFJF,oBAAoB,GAAAD,IAAA,CAApBC,oBAAoB,EACpBC,IAAI,GAAAF,IAAA,CAAJE,IAAI;UAEJ,IAAI,EAACA,IAAI,KAAJA,IAAAA,IAAAA,IAAI,eAAJA,IAAI,CAAEX,WAAW,CAAE,EAAA;AACtBoB,YAAAA,qBAAqB,EAAE;AACvB;YACApB,WAAW,GAAGqB,MAAM,CAACrB,WAAW;AAClC;UAEA,IAAIsB,OAAA,CAAOX,IAAI,KAAA,IAAA,IAAJA,IAAI,KAAJA,MAAAA,GAAAA,MAAAA,GAAAA,IAAI,CAAEY,KAAK,CAAA,KAAK,QAAQ,IAAI,EAACZ,IAAI,KAAJA,IAAAA,IAAAA,IAAI,eAAJA,IAAI,CAAEY,KAAK,CAAE,EAAA;YACnDC,eAAe,CAACb,IAAI,KAAJA,IAAAA,IAAAA,IAAI,uBAAJA,IAAI,CAAEY,KAAK,CAAC;AAC9B;AAAC,UAAA,IAEIZ,IAAI,KAAJA,IAAAA,IAAAA,IAAI,KAAJA,MAAAA,IAAAA,IAAI,CAAEc,IAAI,EAAA;AAAAV,YAAAA,QAAA,CAAAE,IAAA,GAAA,CAAA;AAAA,YAAA;AAAA;AAAAF,UAAAA,QAAA,CAAAE,IAAA,GAAA,CAAA;UAAA,OACPS,cAAc,CAAChB,oBAAoB,CAAC;AAAA,QAAA,KAAA,CAAA;UAG5C,IAAI,EAACC,IAAI,KAAA,IAAA,IAAJA,IAAI,KAAA,MAAA,IAAJA,IAAI,CAAEgB,MAAM,CAAA,EAAEC,gBAAgB,EAAE;AAErC,UAAA,IAAI,EAACjB,IAAI,KAAA,IAAA,IAAJA,IAAI,KAAJA,MAAAA,IAAAA,IAAI,CAAEkB,KAAK,CAAA,EAAEC,eAAe,CAACnB,IAAI,KAAJA,IAAAA,IAAAA,IAAI,uBAAJA,IAAI,CAAEoB,QAAQ,CAAC;UAEjD,IAAI,EAACpB,IAAI,KAAA,IAAA,IAAJA,IAAI,KAAA,MAAA,IAAJA,IAAI,CAAEqB,QAAQ,CAAA,EAAEC,4BAA4B,EAAE;UAEnD,IAAIC,OAAO,CAACC,GAAG,CAACC,QAAQ,KAAK,YAAY,EAAEC,oBAAoB,EAAE;AAAC,QAAA,KAAA,EAAA;AAAA,QAAA,KAAA,KAAA;UAAA,OAAAtB,QAAA,CAAAuB,IAAA,EAAA;AAAA;AAAA,KAAA,EAAA9B,OAAA,CAAA;GACnE,CAAA,CAAA;AAAA,EAAA,OAAAN,sBAAA,CAAAC,KAAA,CAAA,IAAA,EAAAC,SAAA,CAAA;AAAA;;;;"}
|
package/initializers.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bigbinary/neeto-commons-frontend",
|
|
3
|
-
"version": "4.13.
|
|
3
|
+
"version": "4.13.101",
|
|
4
4
|
"description": "A package encapsulating common code across neeto projects including initializers, utility functions, common components and hooks and so on.",
|
|
5
5
|
"repository": "git@github.com:bigbinary/neeto-commons-frontend.git",
|
|
6
6
|
"author": "Amaljith K <amaljith.k@bigbinary.com>",
|