diff --git a/README.md b/README.md index aa11d36a1..2424bb475 100644 --- a/README.md +++ b/README.md @@ -40,11 +40,8 @@ designed to make coding as simple and fun as playing a video game. 4. Uncompromised local development experience. 5. Support for pluggable remote back-ends. 6. Phoenix core will work from a static web server. -7. Source code in release builds will always be auditable and readable from dev-tools. - -AGPL/Libre license guards your right to audit and change code that handles your data. -Phoenix usually loads up in under one second and loading it faster at the expense of making it hard -to read and develop is a noop. We prioritize simplicity and eaze of development. +7. Compile step less build process. Code changes in phoenix do not need to be recompiled for most cases for development. +8. Prioritize simplicity and ease of development. ## Contributing/ Feedback * [Get in touch with our community](https://github.com/phcode-dev/phoenix/discussions). @@ -111,10 +108,10 @@ It has much better debug UX and fixing it directly in the browser will almost ce 2. Run the unit tests using format: `npm run test<*>Dist`. Eg. `npm run testChromiumDist`. 3. Run the integration tests using the format: `npx cross-env TEST_ENV= npm run test<*>Dist`. Eg. `npx cross-env TEST_ENV=mainview npm run testChromiumDist`. -#### To run tests against these stacks go to the following url: -* dev: https://dev.phcode.dev/test/SpecRunner.html -* staging: https://staging.phcode.dev/test/SpecRunner.html -* prod: https://phcode.dev/test/SpecRunner.html +#### To run tests against these dev/staging/prod in the browser +1. Build the release using `npm run release:`. Eg: `npm run release:dev` +2. Run `npm run serve` to start the server. +3. Go to `http://localhost:8000/dist-test/test/SpecRunner.html` in the browser to run tests that was built for the stage above. ## Browsing the virtual file system To view/edit the files in the browser virtual file system in Phoenix: @@ -123,39 +120,16 @@ To view/edit the files in the browser virtual file system in Phoenix: ## Clean and reset builds * clean builds only: `npm run clean` -## Previewing changes in dev and staging -One a pull request is merged, it will be automatically deployed to dev.phcode.dev . To view the changes: -1. goto https://dev.phcode.dev/devEnable.html and click `enable dev.phcode.dev` . only needs to be done once. -2. goto https://dev.phcode.dev to preview your changes. If it is a recent change, you might need to wait for -up to 15 minutes before the changes are deployed to the dev stage. Reload page a few times to get the latest -dev build and reset cached content. - -The process is the same for `staging.phcode.dev`. Builds that are verified in development will be pushed -periodically to staging. To view staging: -1. goto https://staging.phcode.dev/devEnable.html and click `enable staging.phcode.dev` . only needs to be done once. -2. goto https://staging.phcode.dev to preview your changes. If it is a recent change, you might need to wait for - up to 15 minutes before the changes are deployed to the dev stage. Reload page a few times to get the latest - dev build and reset cached content. - -## Deployment to phcode.dev -* All changes pushed to the main branch are automatically published to https://dev.phcode.dev -* To publish the changes to https://staging.phcode.dev , push changes to the `staging` branch in this repo with a pull request. -* Once the changes are validated and tested, trigger a prod deployment by pushing to the `prod` branch. - ## Acknowledgements * Phoenix is based on the Brackets code editor by Adobe. Find out more on [Adobe Brackets here](https://github.com/adobe/brackets/). * Our main code editor library https://codemirror.net/ * Inspired by previous work from the [Mozilla Thimble](https://github.com/mozilla/thimble.mozilla.org) project to port brackets to the web. https://github.com/mozilla/brackets * In browser server based on [nohost](https://github.com/humphd/nohost) by https://github.com/humphd/ - ## License -Discussion: https://github.com/phcode-dev/phoenix/issues/184 - GNU AGPL-3.0 License Copyright (c) 2021 - present Core.ai - Based on Backets, Copyright (c) 2012 Adobe Systems Incorporated and the brackets.io community This program is free software: you can redistribute it and/or modify diff --git a/src/LiveDevelopment/BrowserScripts/LiveDevProtocolRemote.js b/src/LiveDevelopment/BrowserScripts/LiveDevProtocolRemote.js index dc42de724..760baec58 100644 --- a/src/LiveDevelopment/BrowserScripts/LiveDevProtocolRemote.js +++ b/src/LiveDevelopment/BrowserScripts/LiveDevProtocolRemote.js @@ -342,10 +342,15 @@ */ message: function (msgStr) { const msg = JSON.parse(msgStr); + _setPCommReady(); if(msg && typeof msg === "object" && msg.method === "PhoenixComm.execLPFn") { _onLPFnTrigger(msg.fnName, msg.params); return; } + if(msg && typeof msg === "object" && msg.method === "phoenixFnResponse") { + _onPhoenixExecResponse(msg.fnName, msg.fnExecID, msg.resolveWith, msg.rejectWith); + return; + } // delegates handling/routing to MessageBroker. MessageBroker.trigger(msg); }, @@ -381,15 +386,75 @@ } } + let currentFnExecID = 1; + let lpCommReady = false; + const pendingExecPromises = new Map(); + let queuedExecRequests = []; // array of { fnName, paramObj, fnExecID } + + /** + * Sends immediately if ready, else queues for later replay. + * @param {{execFnName:string, paramObj:any, fnExecID:number}} payload + */ + function _sendOrQueueExec(payload) { + if (lpCommReady) { + MessageBroker.send(payload); + } else { + queuedExecRequests.push(payload); + } + } + + /** Flush queued execPhoenixFn calls once LP comm becomes ready. */ + function _flushQueuedExecRequests() { + if (!lpCommReady || queuedExecRequests.length === 0) { + return; + } + // Preserve order + for (let i = 0; i < queuedExecRequests.length; i++) { + MessageBroker.send(queuedExecRequests[i]); + } + queuedExecRequests = []; + } + const PhoenixComm = { registerLpFn: function (fnName, fn) { if(registeredPhoenixCommFns[fnName]){ throw new Error(`Function "${fnName}" already registered with PhoenixComm`); } registeredPhoenixCommFns[fnName] = fn; + }, + execPhoenixFn: function (fnName, paramObj) { + return new Promise((resolve, reject) => { + const fnExecID = currentFnExecID++; + pendingExecPromises.set(fnExecID, { resolve, reject }); + _sendOrQueueExec({ + execFnName: fnName, + paramObj, + fnExecID + }); + }); } }; + function _setPCommReady() { + lpCommReady = true; + _flushQueuedExecRequests(); + } + + PhoenixComm.registerLpFn("PH_LP_COMM_READY", _setPCommReady); + + function _onPhoenixExecResponse(fnName, fnExecID, resolveWith, rejectWith) { + const pendingPromise = pendingExecPromises.get(fnExecID); + if(!pendingPromise) { + console.error(`execPhoenixFn: No response promise found! for ${fnName}: ${fnExecID}`); + } + pendingExecPromises.delete(fnExecID); + if(rejectWith) { + pendingPromise.reject(rejectWith); + } else { + pendingPromise.resolve(resolveWith); + } + } + global._Brackets_LiveDev_PhoenixComm = PhoenixComm; window.addEventListener('load', function () { diff --git a/src/LiveDevelopment/MultiBrowserImpl/protocol/LiveDevProtocol.js b/src/LiveDevelopment/MultiBrowserImpl/protocol/LiveDevProtocol.js index 37407dc8f..eb5a61046 100644 --- a/src/LiveDevelopment/MultiBrowserImpl/protocol/LiveDevProtocol.js +++ b/src/LiveDevelopment/MultiBrowserImpl/protocol/LiveDevProtocol.js @@ -317,6 +317,10 @@ define(function (require, exports, module) { } else if (messageID) { processedMessageIDs.set(messageID, true); } + if(msg && typeof msg === "object" && msg.execFnName) { + _executePhoenixFn(msg.execFnName, msg.paramObj, msg.fnExecID, clientId); + return; + } if(_livePreviewMessageHandler) { let preventDefault = _livePreviewMessageHandler(msg); if(preventDefault){ @@ -405,6 +409,7 @@ define(function (require, exports, module) { clientId: clientId, url: url }); + triggerLPFn("PH_LP_COMM_READY", "", clientId); } /** @@ -585,6 +590,58 @@ define(function (require, exports, module) { registeredFunctions[fnName] = fn; } + function _toErrorString(e) { + if (e instanceof Error) { + if (e.stack) { + return `${e.message}\n${e.stack}`; + } + return e.message || String(e); + } + return String(e); + } + + function _executePhoenixFn(fnName, params, fnExecID, clientId) { + try{ + if(registeredFunctions[fnName]){ + const response = registeredFunctions[fnName](params, clientId); + if(response instanceof Promise) { + response.then((resolveValue)=>{ + _transport.send([clientId], JSON.stringify({ + method: "phoenixFnResponse", + fnName, fnExecID, + resolveWith: resolveValue + })); + }).catch(err => { + _transport.send([clientId], JSON.stringify({ + method: "phoenixFnResponse", + fnName, fnExecID, + rejectWith: _toErrorString(err) + })); + }); + return; + } + _transport.send([clientId], JSON.stringify({ + method: "phoenixFnResponse", + fnName, fnExecID, + resolveWith: response + })); + } else { + console.error(`Function "${fnName}" not registered with registerPhoenixFn`); + _transport.send([clientId], JSON.stringify({ + method: "phoenixFnResponse", + fnName, fnExecID, + rejectWith: `Function "${fnName}" not registered with registerPhoenixFn` + })); + } + } catch (err) { + _transport.send([clientId], JSON.stringify({ + method: "phoenixFnResponse", + fnName, fnExecID, + rejectWith: _toErrorString(err) + })); + } + } + /** * Triggers a named API function in the Live Preview for one or more clients * in a **fire-and-forget** manner. @@ -636,9 +693,6 @@ define(function (require, exports, module) { }); } - - window.ee= triggerLPFn; // todo remove this once all usages are migrated to execLPFn - /** * Closes the connection to the given client. Proxies to the transport. * @param {number} clientId diff --git a/tracking-repos.json b/tracking-repos.json index e85f68627..7c1589391 100644 --- a/tracking-repos.json +++ b/tracking-repos.json @@ -1,5 +1,5 @@ { "phoenixPro": { - "commitID": "1bda799c4c6221d33c85e218226c9d66707e873d" + "commitID": "3826a7180c9463ecf02f4051e78696d503c7c6f1" } }