From 17d7eaec7bcecbe34012153afe1bf2ca8513cef3 Mon Sep 17 00:00:00 2001 From: Richard Newman Date: Wed, 21 Sep 2016 14:07:04 -0700 Subject: [PATCH] Add a babelified test file, Webpack the add-on, and make the JS API work. We concatenate a simple setTimeout monkeypatch onto the add-on itself. --- .babelrc | 27 ++++++++ .gitignore | 7 +- addon/.babelrc | 6 ++ addon/build.sh | 2 + addon/index.js | 15 ----- addon/package.json | 28 +++++--- addon/{ => release}/README.md | 0 addon/release/data/datomish-48.png | Bin 0 -> 1082 bytes addon/release/package.json | 15 +++++ addon/release/run.sh | 1 + addon/src/index.js | 101 +++++++++++++++++++++++++++++ addon/src/wrapper.prefix | 4 ++ addon/webpack.config.js | 21 ++++++ package.json | 23 ++++++- src/common/datomish/js.cljs | 76 ++++++++++++++-------- test/js/tests.js | 46 +++++++++++++ 16 files changed, 317 insertions(+), 55 deletions(-) create mode 100644 .babelrc create mode 100644 addon/.babelrc create mode 100755 addon/build.sh delete mode 100644 addon/index.js rename addon/{ => release}/README.md (100%) create mode 100644 addon/release/data/datomish-48.png create mode 100644 addon/release/package.json create mode 100755 addon/release/run.sh create mode 100644 addon/src/index.js create mode 100644 addon/src/wrapper.prefix create mode 100644 addon/webpack.config.js create mode 100644 test/js/tests.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 00000000..d707934a --- /dev/null +++ b/.babelrc @@ -0,0 +1,27 @@ +{ + "env": { + "production": { + "presets": ["react", "react-optimize"] + }, + "development": { + "presets": ["react"] + }, + "test": { + "presets": ["react"] + } + }, + "only": [ + "test/js/**" + ], + "plugins": [ + "transform-es2015-destructuring", + "transform-es2015-parameters", + "transform-es2015-modules-commonjs", + "transform-async-to-generator", + "transform-object-rest-spread", + "transform-class-properties", + "transform-runtime" + ], + "sourceMaps": "inline", + "retainLines": true +} diff --git a/.gitignore b/.gitignore index 76f17ade..bb6ab5a9 100644 --- a/.gitignore +++ b/.gitignore @@ -28,5 +28,8 @@ pom.xml.asc /release-node /release-node/datomish.js /release-node/datomish.bare.js -/addon/datomish-test.xpi -/addon/datomish.js +/addon/built/index.js +/addon/node_modules/ +/addon/release/datomish-test.xpi +/addon/release/datomish.js +/addon/release/index.js diff --git a/addon/.babelrc b/addon/.babelrc new file mode 100644 index 00000000..a0dc53f5 --- /dev/null +++ b/addon/.babelrc @@ -0,0 +1,6 @@ +{ + "presets": ["es2015"], + "plugins": [ + "transform-async-to-generator" + ] +} diff --git a/addon/build.sh b/addon/build.sh new file mode 100755 index 00000000..493c055d --- /dev/null +++ b/addon/build.sh @@ -0,0 +1,2 @@ +node_modules/.bin/webpack -p +cat src/wrapper.prefix built/index.js > release/index.js diff --git a/addon/index.js b/addon/index.js deleted file mode 100644 index 765cb243..00000000 --- a/addon/index.js +++ /dev/null @@ -1,15 +0,0 @@ -var self = require("sdk/self"); - -console.log("Datomish Test"); -console.log("This: " + this); - -var datomish = require("datomish.js"); -datomish.open("/tmp/foobar.db").then(function (db) { - console.log("Got " + db); - try { - db.close(); - console.log("Closed."); - } catch (e) { - console.log("Couldn't close: " + e); - } -}); diff --git a/addon/package.json b/addon/package.json index 6a190546..c2e4d9f0 100644 --- a/addon/package.json +++ b/addon/package.json @@ -1,15 +1,23 @@ { - "title": "Datomish Test", - "name": "datomish-test", - "version": "0.0.1", - "description": "An example add-on that loads Datomish on top of Sqlite.jsm.", + "name": "datomish-example", + "version": "1.0.0", + "description": "A test add-on for Datomish and Firefox.", "main": "index.js", - "author": "Richard Newman ", - "engines": { - "firefox": ">=48.0a1" + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" }, + "author": "", "license": "MPL-2.0", - "keywords": [ - "jetpack" - ] + "devDependencies": { + "babel": "^6.5.2", + "babel-cli": "^6.14.0", + "babel-core": "^6.14.0", + "babel-loader": "^6.2.5", + "babel-plugin-transform-async-to-generator": "^6.8.0", + "babel-preset-es2015": "^6.14.0", + "webpack": "^1.13.2" + }, + "dependencies": { + "babel-polyfill": "^6.13.0" + } } diff --git a/addon/README.md b/addon/release/README.md similarity index 100% rename from addon/README.md rename to addon/release/README.md diff --git a/addon/release/data/datomish-48.png b/addon/release/data/datomish-48.png new file mode 100644 index 0000000000000000000000000000000000000000..42b9cb678787a5dd3a81dd86bebd51c225fc3447 GIT binary patch literal 1082 zcmeAS@N?(olHy`uVBq!ia0vp^20*OG!2%?YTn~BNn{1`6_P!I zd>I(3)EF2VS{N990fib~Fffz?HNHwHx@oI*`2amCO38HbXa+=a^?Od zuz0ZMqN)1HO>;NZrWl>vHD~kHXHR`5s3&Tra853Ik~U4jd*a(| z+EH6x*Gx>0H)irP1Vb!|n#=IvQ50jN=s@+Ii>~!5NhimFC z!`4rJwAcFq<*v%Jq!Dt;(-iz$1ROMK*Nd#mVQIYJML|wahgLeY~_ztej>ulIvgW8r#D@5?;qnVm~!@J%mqIO_EBezdUo z8S6uzKJHpPQNO}&*VSJ}mU7<~3g0W^e7h@{eaemnr=O{4e`zh;t=l)lpQTM^Qg==@ zZ`P#ge*d;e&fguqQ%n8m-u&LB8;r{K#*2qGz?7V1?Qt zdEf=*?4!Azac&33yd<+RV=8vjpz z#$l)3Kb}TEog^V#exNoi`aR=$&CvD6k6-j`@R}Rf*wT{MzpUzaebvviWqYkfcCT=L zFS_S1SJ;N8|04gG7`AFGkO-LQ0L)9OC9V-ADTyViR>?)FK#IZ0z|c(Bz*5)1EX2^< v%EZhH#5K1vFt8E*Jr_koZhlH;S|t_@rVtG^3nKRbH86O(`njxgN@xNAGvwj5 literal 0 HcmV?d00001 diff --git a/addon/release/package.json b/addon/release/package.json new file mode 100644 index 00000000..6a190546 --- /dev/null +++ b/addon/release/package.json @@ -0,0 +1,15 @@ +{ + "title": "Datomish Test", + "name": "datomish-test", + "version": "0.0.1", + "description": "An example add-on that loads Datomish on top of Sqlite.jsm.", + "main": "index.js", + "author": "Richard Newman ", + "engines": { + "firefox": ">=48.0a1" + }, + "license": "MPL-2.0", + "keywords": [ + "jetpack" + ] +} diff --git a/addon/release/run.sh b/addon/release/run.sh new file mode 100755 index 00000000..40114394 --- /dev/null +++ b/addon/release/run.sh @@ -0,0 +1 @@ +jpm run -b /Applications/FirefoxNightly.app/ diff --git a/addon/src/index.js b/addon/src/index.js new file mode 100644 index 00000000..9b8817da --- /dev/null +++ b/addon/src/index.js @@ -0,0 +1,101 @@ +var self = require("sdk/self"); +var buttons = require('sdk/ui/button/action'); +var tabs = require('sdk/tabs'); + +var datomish = require("datomish.js"); + +var schema = { + "name": "pages", + "attributes": [ + {"name": "page/url", + "type": "string", + "cardinality": "one", + "unique": "identity", + "doc": "A page's URL."}, + {"name": "page/title", + "type": "string", + "cardinality": "one", + "fulltext": true, + "doc": "A page's title."}, + {"name": "page/content", + "type": "string", + "cardinality": "one", // Simple for now. + "fulltext": true, + "doc": "A snapshot of the page's content. Should be plain text."}, + ] +}; + +async function initDB(path) { + let db = await datomish.open(path); + await db.ensureSchema(schema); + return db; +} + +async function findURLs(db) { + let query = `[:find ?page ?url ?title :in $ :where [?page :page/url ?url][(get-else $ ?page :page/title "") ?title]]`; + let options = new Object(); + options["limit"] = 10; + return datomish.q(db.db(), query, options); +} + +async function findPagesMatching(db, string) { + let query = + `[:find ?url ?title + :in $ ?str + :where + [(fulltext $ :any ?str) [[?page]]] + [?page :page/url ?url] + [(get-else $ ?page :page/title "") ?title]]`; + return datomish.q(db.db(), query, {"limit": 10, "inputs": {"str": string}}); +} + +async function savePage(db, url, title, content) { + let txResult = await db.transact([{"db/id": 55, + "page/title": title, + "page/url": url + // "page/starred": true + }]); + return txResult; +} + +async function handleClick(state) { + let db = await datomish.open("/tmp/testing.db"); + await db.ensureSchema(schema); + let txResult = await savePage(db, tabs.activeTab.url, tabs.activeTab.title, "Content goes here"); + console.log("Transaction returned " + JSON.stringify(txResult)); + console.log("Transaction instant: " + txResult.txInstant); + let results = await findURLs(db); //datomish.q(db.db(), "[:find ?url :in $ :where [?e :page/url ?url]]"); + results = results.map(r => r[1]); + console.log("Query results: " + JSON.stringify(results)); + let pages = await findPagesMatching(db, "goes"); + console.log("Pages: " + JSON.stringify(pages)); + await db.close(); +} + +/* +async function handleClick(state) { + console.log("Handling click: " + state); + let tab = tabs.activeTab; + console.log("Active tab: " + tab); + console.log("Active tab: " + tab.url); + console.log("Active tab: " + tab.title); + let db = await initDB("/tmp/datomish.db"); + console.log("Opened DB: " + db); + await savePage(db, tab.url, tab.title, "Content goes here."); + console.log("Saved page."); + + let urls = await findURLs(db); + console.log("URLs: " + JSON.stringify(urls)); + + let results = await findPagesMatching(db, "goes"); + console.log("Pages: " + JSON.stringify(results)); + await db.close(); +} +*/ + +var button = buttons.ActionButton({ + id: "datomish-save", + label: "Save Page", + icon: "./datomish-48.png", + onClick: handleClick +}); diff --git a/addon/src/wrapper.prefix b/addon/src/wrapper.prefix new file mode 100644 index 00000000..f69c8236 --- /dev/null +++ b/addon/src/wrapper.prefix @@ -0,0 +1,4 @@ +// Monkeypatch. +var { setTimeout } = require("sdk/timers"); +this.setTimeout = setTimeout; + diff --git a/addon/webpack.config.js b/addon/webpack.config.js new file mode 100644 index 00000000..f82bede4 --- /dev/null +++ b/addon/webpack.config.js @@ -0,0 +1,21 @@ + +module.exports = { + entry: ['babel-polyfill', './src/index.js'], + output: { + filename: 'built/index.js' + }, + target: 'webworker', + externals: { + 'datomish.js': 'commonjs datomish.js', + 'sdk/self': 'commonjs sdk/self', + 'sdk/ui/button/action': 'commonjs sdk/ui/button/action', + 'sdk/tabs': 'commonjs sdk/tabs' + }, + module: { + loaders: [{ + test: /\.js?$/, + exclude: /(node_modules)|(wrapper.prefix)/, + loader: 'babel' + }] + } +} diff --git a/package.json b/package.json index d20d47ea..696f3d89 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,26 @@ "thenify-all": "^1.6.0", "ws": "1.1.1" }, + "scripts": { + "test": "babel-node test/js/tests.js" + }, "devDependencies": { + "babel-cli": "^6.14.0", + "babel-core": "6.14.0", + "babel-eslint": "6.1.2", + "babel-loader": "6.2.5", + "babel-plugin-transform-async-to-generator": "6.8.0", + "babel-plugin-transform-class-properties": "6.11.5", + "babel-plugin-transform-es2015-destructuring": "6.9.0", + "babel-plugin-transform-es2015-modules-commonjs": "6.11.5", + "babel-plugin-transform-es2015-parameters": "6.11.4", + "babel-plugin-transform-object-rest-spread": "6.8.0", + "babel-plugin-transform-runtime": "6.12.0", + "babel-polyfill": "6.13.0", + "babel-preset-react": "6.11.1", + "babel-preset-react-optimize": "1.0.1", + "babel-register": "6.14.0", + "babel-runtime": "6.11.6", "tmp": "0.0.28" }, "repository": { @@ -27,5 +46,7 @@ }, "homepage": "https://github.com/mozilla/datomish#readme", "main": "./datomish.js", - "files": ["datomish.js"] + "files": [ + "datomish.js" + ] } diff --git a/src/common/datomish/js.cljs b/src/common/datomish/js.cljs index 0fd7aa5a..fc7a39cb 100644 --- a/src/common/datomish/js.cljs +++ b/src/common/datomish/js.cljs @@ -10,6 +10,7 @@ [cljs.core.async :as a :refer [take! !]] [cljs.reader] [cljs-promises.core :refer [promise]] + [datomish.cljify :refer [cljify]] [datomish.db :as db] [datomish.db-factory :as db-factory] [datomish.pair-chan] @@ -18,14 +19,17 @@ [datomish.js-sqlite :as js-sqlite] [datomish.transact :as transact])) -(defn- take-pair-as-promise! [ch] +(defn- take-pair-as-promise! [ch f] ;; Just like take-as-promise!, but aware that it's handling a pair channel. + ;; Also converts values, if desired. (promise (fn [resolve reject] (letfn [(split-pair [[v e]] (if e - (reject e) - (resolve v)))] + (do + (println "Got error:" e) + (reject e)) + (resolve (f v))))] (cljs.core.async/take! ch split-pair))))) ;; Public API. @@ -35,32 +39,46 @@ (defn ^:export q [db find options] (let [find (cljs.reader/read-string find) - options (js->clj options)] + opts (cljify options)] + (println "Running query " (pr-str find) (pr-str {:foo find}) (pr-str opts)) (take-pair-as-promise! - (db/js res))) + identity))) (defn ^:export ensure-schema [conn simple-schema] - (let [simple-schema (js->clj simple-schema)] - (println "simple-schema: " (pr-str simple-schema)) + (let [simple-schema (cljify simple-schema) + datoms (simple-schema/simple-schema->schema simple-schema)] + (println "Transacting schema datoms" (pr-str datoms)) (take-pair-as-promise! (transact/schema simple-schema))))) + datoms) + clj->js))) -(defn js->tx-data [tx-data] - ;; Objects to maps. - ;; Arrays to arrays. - ;; RHS stringsā€¦ well, some of them will be richer types. - ;; TODO - (println "Converting" (pr-str tx-data) "to" (pr-str (js->clj tx-data :keywordize-keys true))) - (println "Converting" (pr-str tx-data) "to" (pr-str (js->clj tx-data :keywordize-keys true))) - (js->clj tx-data)) +(def js->tx-data cljify) + +(def ^:export tempid (partial db/id-literal :db.part/user)) (defn ^:export transact [conn tx-data] ;; Expects a JS array as input. - (let [tx-data (js->tx-data tx-data)] - (take-pair-as-promise! - (transact/tx-data tx-data)] + (println "Transacting:" (pr-str tx-data)) + (take-pair-as-promise! + (go-pair + (let [tx-result (js)) + (catch js/Error e + (println "Error in transact:" e)))) (defn ^:export open [path] ;; Eventually, URI. For now, just a plain path (no file://). @@ -69,11 +87,15 @@ (let [conn (js - {:conn c - :ensureSchema (fn [simple-schema] (ensure-schema c simple-schema)) - :transact (fn [tx-data] (transact c tx-data)) - :q (fn [find options] (q (transact/db c) find options)) - :close (fn [] (db/close-db db)) - :toString (fn [] (str "#")) - :path path})))))) + (clj->js + ;; We pickle the connection as a thunk here so it roundtrips through JS + ;; without incident. + {:conn (fn [] c) + :roundtrip (fn [x] (clj->js (cljify x))) + :db (fn [] (transact/db c)) + :ensureSchema (fn [simple-schema] (ensure-schema c simple-schema)) + :transact (fn [tx-data] (transact c tx-data)) + :close (fn [] (db/close-db db)) + :toString (fn [] (str "#")) + :path path})))) + identity)) diff --git a/test/js/tests.js b/test/js/tests.js new file mode 100644 index 00000000..e9e20a44 --- /dev/null +++ b/test/js/tests.js @@ -0,0 +1,46 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +var datomish = require("../../release-node/datomish.js"); + +var schema = { + "name": "pages", + "attributes": [ + {"name": "page/url", + "type": "string", + "cardinality": "one", + "unique": "identity", + "doc": "A page's URL."}, + {"name": "page/title", + "type": "string", + "cardinality": "one", + "doc": "A page's title."}, + {"name": "page/starred", + "type": "boolean", + "cardinality": "one", + "doc": "Whether the page is starred."}, + {"name": "page/visit", + "type": "ref", + "cardinality": "many", + "doc": "A visit to the page."} + ] +}; + +async function testOpen() { + let db = await datomish.open("/tmp/testing.db"); + await db.ensureSchema(schema); + let txResult = await db.transact([{"db/id": 55, + "page/url": "http://foo.com/bar", + "page/starred": true}]); + console.log("Transaction returned " + JSON.stringify(txResult)); + console.log("Transaction instant: " + txResult.txInstant); + let results = await datomish.q(db.db(), "[:find ?url :in $ :where [?e :page/url ?url]]") + results = results.map(r => r[0]); + console.log("Query results: " + JSON.stringify(results)); + await db.close(); +} + +testOpen() +.then((r) => console.log("Done.")) +.catch((e) => console.log("Failure: " + e.stack));