diff --git a/src/app/package-lock.json b/src/app/package-lock.json index 09283ed..35f43d0 100644 --- a/src/app/package-lock.json +++ b/src/app/package-lock.json @@ -2040,6 +2040,11 @@ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, "default-gateway": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.1.2.tgz", @@ -2350,6 +2355,31 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, + "escodegen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz", + "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==", + "requires": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true + } + } + }, "eslint-scope": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", @@ -2359,6 +2389,11 @@ "estraverse": "^4.1.1" } }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, "esrecurse": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", @@ -2626,6 +2661,11 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, "fastparse": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", @@ -4158,6 +4198,15 @@ "invert-kv": "^2.0.0" } }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, "loader-runner": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", @@ -4747,6 +4796,19 @@ "is-wsl": "^1.1.0" } }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, "original": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", @@ -5076,6 +5138,11 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + }, "pretty-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz", @@ -6376,6 +6443,14 @@ "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "requires": { + "prelude-ls": "~1.1.2" + } + }, "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", @@ -6907,6 +6982,11 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + }, "worker-farm": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", diff --git a/src/app/package.json b/src/app/package.json index eb7277b..214444a 100644 --- a/src/app/package.json +++ b/src/app/package.json @@ -23,6 +23,9 @@ "@babel/register": "^7.0.0", "babel-loader": "^8.0.5", "css-loader": "^2.1.0", + "escodegen": "^1.11.1", + "esprima": "^4.0.1", + "estraverse": "^4.2.0", "html-webpack-plugin": "^3.2.0", "react": "^16.8.3", "react-dom": "^16.8.3", diff --git a/src/app/parser.js b/src/app/parser.js new file mode 100644 index 0000000..338b03d --- /dev/null +++ b/src/app/parser.js @@ -0,0 +1,172 @@ +const esprima = require('esprima'); +const fs = require('fs'); +const estraverse = require('estraverse'); +const escodegen = require('escodegen'); +const path = require('path'); +const _ = require('lodash'); + +// react files +const useReducerfile = '/node_modules/react/cjs/react.development.js'; +const commitAllHostEffectsfile = '/node_modules/react-dom/cjs/react-dom.development.js'; + +// generated file names +const genReactDev = 'generatedReact.development.js'; +const genReactDom = 'generatedReact-dom.development.js'; + +// convert file to string and parse +function parseFile(file) { + const dir = path.join(__dirname, file); + const fileString = fs.readFileSync(dir, { encoding: 'utf-8' }); + const ast = esprima.parseModule(fileString); + return ast; +} +// declare functions to insert +// TODO: Un-comment timeTravelTracker +function useReducerReplacement() { + let dispatcher = resolveDispatcher(); + function reducerWithTracker(state, action) { + const newState = reducer(state, action); + // timeTravelTracker[timeTravelTracker.length - 1].actionDispatched = true; + window.postMessage({ + type: 'DISPATCH', + data: { + state: newState, + action, + }, + }); + return newState; + } + return dispatcher.useReducer(reducerWithTracker, initialArg, init); +} +function commitAllHostEffectsReplacement() { + while (nextEffect !== null) { + { + setCurrentFiber(nextEffect); + } + recordEffect(); + + var effectTag = nextEffect.effectTag; + + if (effectTag & ContentReset) { + commitResetTextContent(nextEffect); + } + + if (effectTag & Ref) { + var current$$1 = nextEffect.alternate; + if (current$$1 !== null) { + commitDetachRef(current$$1); + } + } + + // The following switch statement is only concerned about placement, + // updates, and deletions. To avoid needing to add a case for every + // possible bitmap value, we remove the secondary effects from the + // effect tag and switch on that value. + var primaryEffectTag = effectTag & (Placement | Update | Deletion); + switch (primaryEffectTag) { + case Placement: + { + //editbyme + window.postMessage({ + type: 'EFFECT', + data: { + primaryEffectTag: 'PLACEMENT', + effect: _.cloneDeep(nextEffect) + } + }); + + commitPlacement(nextEffect); + // Clear the "placement" from effect tag so that we know that this is inserted, before + // any life-cycles like componentDidMount gets called. + // TODO: findDOMNode doesn't rely on this any more but isMounted + // does and isMounted is deprecated anyway so we should be able + // to kill this. + nextEffect.effectTag &= ~Placement; + break; + } + case PlacementAndUpdate: + { + // Placement + commitPlacement(nextEffect); + // Clear the "placement" from effect tag so that we know that this is inserted, before + // any life-cycles like componentDidMount gets called. + nextEffect.effectTag &= ~Placement; + + // Update + var _current = nextEffect.alternate; + commitWork(_current, nextEffect); + break; + } + case Update: + { + //editbyme + window.postMessage({ + type: 'EFFECT', + data: { + primaryEffectTag: 'UPDATE', + effect: _.cloneDeep(nextEffect), + current: _.cloneDeep(nextEffect.alternate), + }, + }); + + var _current2 = nextEffect.alternate; + commitWork(_current2, nextEffect); + break; + } + case Deletion: + { + //editbyme + window.postMessage({ + type: 'EFFECT', + data: { + primaryEffectTag: 'DELETION', + effect: _.cloneDeep(nextEffect), + }, + }); + + commitDeletion(nextEffect); + break; + } + } + nextEffect = nextEffect.nextEffect; + } + + { + resetCurrentFiber(); + } +} + +// traverse ast to find method and replace body with our node's body +function traverseTree(replacementNode, functionName, ast) { + estraverse.replace(ast, { + enter(node) { + if (node.type === 'FunctionDeclaration') { + if (node.id.name === functionName) { + node.body = replacementNode.body[0].body; + } + } + }, + }); +} + +function generateFile(filename, ast) { + const code = escodegen.generate(ast); + fs.writeFileSync(filename, code, (err) => { + if (err) throw new Error(err.message); + }); +} + +const parseAndGenerate = () => { + // first file + let ast = parseFile(useReducerfile); + const injectableUseReducer = esprima.parseScript(useReducerReplacement.toString()); + traverseTree(injectableUseReducer, 'useReducer', ast); + generateFile(genReactDev, ast); + // second file + ast = parseFile(commitAllHostEffectsfile); + const injectableCommitAllHostEffects = esprima.parseScript(commitAllHostEffectsReplacement.toString()); + traverseTree(injectableCommitAllHostEffects, 'commitAllHostEffects', ast); + generateFile(genReactDom, ast); +}; + +module.exports = parseAndGenerate;