diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..51f799b7 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "mixpanel" +} diff --git a/.gitignore b/.gitignore index 3a747149..bdfc9d88 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ node_modules build test/fixtures/build.js npm-debug.log +.DS_Store +/.vscode /isorender diff --git a/devtools/README.md b/devtools/README.md new file mode 100644 index 00000000..c5fdf9b0 --- /dev/null +++ b/devtools/README.md @@ -0,0 +1,9 @@ +# Installing in devmode +* Open chrome://extensions in a chrome tab +* Make sure "developer mode" is checked +* Click on "Load unpacked extension..." and choose the 'devtools' folder in repo + +# Publishing to Chrome store + 1. Someone from mixpanel-chrome-extensions@googlegroups.com can publish this. (@iron-council members are part of thr group) + 2. Run `node run publish-devtools` + 3. Sanity check webstore dashboard at https://chrome.google.com/webstore/developer/dashboard diff --git a/devtools/assets/icon128.png b/devtools/assets/icon128.png new file mode 100644 index 00000000..c8a1767f Binary files /dev/null and b/devtools/assets/icon128.png differ diff --git a/devtools/assets/icon16.png b/devtools/assets/icon16.png new file mode 100644 index 00000000..7692d0df Binary files /dev/null and b/devtools/assets/icon16.png differ diff --git a/devtools/assets/icon48.png b/devtools/assets/icon48.png new file mode 100644 index 00000000..fa0006dd Binary files /dev/null and b/devtools/assets/icon48.png differ diff --git a/devtools/devtools.html b/devtools/devtools.html new file mode 100644 index 00000000..bcf0413f --- /dev/null +++ b/devtools/devtools.html @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/devtools/devtools.js b/devtools/devtools.js new file mode 100644 index 00000000..d948fb51 --- /dev/null +++ b/devtools/devtools.js @@ -0,0 +1,35 @@ +/* global chrome */ +// The function below is executed in the context of the inspected page. +function getPanelElementState() { + if (!window[`__$panelDevToolsReady`] && window.document.body) { + // Chrome extension api doesn't let us know when expression has been edited + // Work around is to refresh the UI with latest state on mouseenter + window.document.body.addEventListener(`mouseenter`, getPanelElementState); + window[`__$panelDevToolsReady`] = true; + } + + // $0 is not available if called via event listeners + const selectedElem = window[`$0`] || window[`__$panelDevToolsLastSelectedElem`]; + + if (selectedElem && selectedElem.update && selectedElem.state) { + window[`__$panelDevToolsLastSelectedElem`] = selectedElem; + selectedElem.update(); // Force a refresh so UI reflects latest state + return selectedElem.state; + } + + // No panel component selected, remove references + window[`__$panelDevToolsLastSelectedElem`] = null; + return {error: `component state not found`}; +} + +chrome.devtools.panels.elements.createSidebarPane(`Panel State`, function(sidebar) { + function updateTabExpression() { + // setExpression just shows result of a nested expression that is editable + // Chrome doesn't expose an API to detect when the result is changed by manual editing + // The work around is to unselect the element and re-select it so $0.update() is called + sidebar.setExpression(`(` + getPanelElementState.toString() + `)()`); + } + + chrome.devtools.panels.elements.onSelectionChanged.addListener(updateTabExpression); + updateTabExpression(); +}); diff --git a/devtools/manifest.json b/devtools/manifest.json new file mode 100644 index 00000000..1164392b --- /dev/null +++ b/devtools/manifest.json @@ -0,0 +1,13 @@ +{ + "version": "1.3", + "manifest_version": 2, + "name": "Panel State", + "description": "Shows Mixpanel's Panel component state", + "devtools_page": "devtools.html", + "author": "dev@mixpanel.com", + "icons": { + "16": "assets/icon16.png", + "48": "assets/icon48.png", + "128": "assets/icon128.png" + } +} \ No newline at end of file diff --git a/package.json b/package.json index 5bf33317..45c7ccb7 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,15 @@ "version": "0.10.0", "description": "Web Components with Virtual DOM: lightweight composable web apps", "main": "build/index.js", + "files": [ + "build" + ], "scripts": { "build": "babel lib -d build && cp -r build/isorender .", "build-test": "webpack --config test/fixtures/webpack.config.js", "docs": "rm -rf docs && jsdoc lib lib/isorender -t node_modules/minami -R README-API.md -d docs", "prepublish": "npm run build", + "publish-devtools": "node scripts/publish-devtools.js", "test": "npm run build-test && npm run test-server && npm run test-browser-local", "test-browser-local": "wct --plugin local test/browser/index.html", "test-browser-p": "wct --plugin local --persistent test/browser/index.html", @@ -47,11 +51,16 @@ "babel-polyfill": "^6.13.0", "babel-preset-es2015": "^6.6.0", "chai": "^3.5.0", + "chrome-store-api": "^1.0.5", + "eslint-config-mixpanel": "^2.4.0", "jsdoc": "^3.4.0", "minami": "^1.1.1", "mocha": "^2.5.3", + "promisify-node": "^0.4.0", + "readline-sync": "^1.4.7", "web-component-tester": "5.0.0", "webcomponents.js": "0.7.22", - "webpack": "1.13.0" + "webpack": "1.13.0", + "zip-folder": "^1.0.0" } } diff --git a/scripts/publish-devtools.js b/scripts/publish-devtools.js new file mode 100644 index 00000000..17beed32 --- /dev/null +++ b/scripts/publish-devtools.js @@ -0,0 +1,59 @@ +/** + * Usage: node scripts/publish-devtools.js + */ +/* global require, __dirname */ +/*eslint no-console: off */ +const promisify = require(`promisify-node`); +const fs = require(`fs`); +const fsPath = require(`path`); +const readlineSync = require(`readline-sync`); +const zipFolder = promisify(require(`zip-folder`)); +const WebstoreApi = require(`chrome-store-api`).Webstore; +const TokenManager = require(`chrome-store-api`).TokenManager; + +const googleOauthUrl = `https://accounts.google.com/o/oauth2`; +const redirectUri = `urn:ietf:wg:oauth:2.0:oob`; // cli app +const webstoreClientId = `839233470602-9vanoggafqre5cpcav7su73dn339ejcb.apps.googleusercontent.com`; +const webstoreClientSecret = `DQA9I0jEDJsh7ndGGjqyd70-`; +const webstoreExtensionId = `mooaaejpdfdebnddpokcdimcobepahlg`; +const devtoolsDir = fsPath.resolve(`${__dirname}/../devtools`); +const devtoolsZipPath = `${devtoolsDir}.zip`; +const manifestPath = `${devtoolsDir}/manifest.json`; + +// Main wrapped in async since top-level await is not supported +(async function() { + try{ + // Write new version to manifest + const manifest = JSON.parse(fs.readFileSync(manifestPath, `utf-8`)); + console.log(`Current manifest:\n`, manifest); + + const newVersion = readlineSync.question(`Enter new version: `); + manifest.version = newVersion; + console.log(`New manifest:\n`, manifest); + fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, ` `), `utf-8`); + + // Zip file + await zipFolder(devtoolsDir, devtoolsZipPath); + console.log(`\nBundling to a zip file`); + const zipContents = fs.readFileSync(devtoolsZipPath); + fs.unlinkSync(devtoolsZipPath); // Delete generated zip + + // Authenticate with webstore + console.log(`NOTE: Only users of mixpanel-chrome-extensions@googlegroups.com can publish this item.`); + console.log(`Paste following url in browser, authenticate and paste generate code\n`); + console.log(`${googleOauthUrl}/auth?response_type=code&scope=https://www.googleapis.com/auth/chromewebstore&client_id=${webstoreClientId}&redirect_uri=${redirectUri}`); + const oauthCode = readlineSync.question(`\ncode: `); + const api = new WebstoreApi(new TokenManager(oauthCode, webstoreClientId, webstoreClientSecret)); + + // Upload new item and publish + console.log(`\nUploading and publishing ...`); + await api.update(webstoreExtensionId, zipContents); + await api.publish(webstoreExtensionId); + console.log(`Published. Item should be available in a few minutes`); + console.log(`Check status at https://chrome.google.com/webstore/developer/dashboard`); + + } catch (err) { + console.error(err); + } +})(); +