{"html_url": "https://github.com/simonw/datasette/issues/983#issuecomment-766536076", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 766536076, "node_id": "MDEyOklzc3VlQ29tbWVudDc2NjUzNjA3Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2021-01-25T04:43:53Z", "updated_at": "2021-01-25T04:43:53Z", "author_association": "OWNER", "body": "... actually not going to include this in 0.54, I need to write a couple of plugins myself using it before I even make it available in preview.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 712260429, "label": "JavaScript plugin hooks mechanism similar to pluggy"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/983#issuecomment-752759885", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 752759885, "node_id": "MDEyOklzc3VlQ29tbWVudDc1Mjc1OTg4NQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-30T21:11:52Z", "updated_at": "2020-12-30T21:14:00Z", "author_association": "OWNER", "body": "262 bytes if I remove the parameter introspection code, instead requiring plugin authors to specify the arguments they take:\r\n```javascript\r\nwindow.datasette = window.datasette || {};\r\nwindow.datasette.plugins = (() => {\r\n var registry = {};\r\n return {\r\n register: (hook, fn, parameters) => {\r\n if (!registry[hook]) {\r\n registry[hook] = [];\r\n }\r\n registry[hook].push([fn, parameters]);\r\n },\r\n call: (hook, args) => {\r\n args = args || {};\r\n var results = [];\r\n (registry[hook] || []).forEach(([fn, parameters]) => {\r\n /* Call with the correct arguments */\r\n var callWith = parameters.map(parameter => args[parameter]);\r\n var result = fn.apply(fn, callWith);\r\n if (result) {\r\n results.push(result);\r\n }\r\n });\r\n return results;\r\n }\r\n };\r\n})();\r\n```\r\n`window.datasette=window.datasette||{},window.datasette.plugins=(()=>{var a={};return{register:(t,e,r)=>{a[t]||(a[t]=[]),a[t].push([e,r])},call:(t,e)=>{e=e||{};var r=[];return(a[t]||[]).forEach(([a,t])=>{var s=t.map(a=>e[a]),d=a.apply(a,s);d&&r.push(d)}),r}}})();`", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 712260429, "label": "JavaScript plugin hooks mechanism similar to pluggy"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/983#issuecomment-753587963", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 753587963, "node_id": "MDEyOklzc3VlQ29tbWVudDc1MzU4Nzk2Mw==", "user": {"value": 154364, "label": "dracos"}, "created_at": "2021-01-03T09:02:50Z", "updated_at": "2021-01-03T10:00:05Z", "author_association": "NONE", "body": "> but I'm already commited to requiring support for () => {} arrow functions\r\n\r\nDon't think you are :) (e.g. gzipped, using arrow functions in my example saves 2 bytes over spelling out function). On FMS, past month, looking at popular browsers, looks like we'd have 95.41% arrow support, 94.19% module support, and 4.58% (mostly IE9/IE11/Safari 9) supporting neither.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 712260429, "label": "JavaScript plugin hooks mechanism similar to pluggy"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/983#issuecomment-744066249", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 744066249, "node_id": "MDEyOklzc3VlQ29tbWVudDc0NDA2NjI0OQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-13T20:47:52Z", "updated_at": "2020-12-13T20:47:52Z", "author_association": "OWNER", "body": "@yozlet just spotted this comment. Wow that is interesting!\r\n\r\nWith the right plugin hooks on the page (see also #987) one relatively simple way to do that could be with bookmarklets - users could install bookmarklets which, when executed against a Datasette page in their browser, use the existing JavaScript plugin integration points to add all kinds of functionality.\r\n\r\nDoing full sandboxing is certainly daunting, but it looks like Figma figured it out so TIL it's technically feasible.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 712260429, "label": "JavaScript plugin hooks mechanism similar to pluggy"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/983#issuecomment-753218817", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 753218817, "node_id": "MDEyOklzc3VlQ29tbWVudDc1MzIxODgxNw==", "user": {"value": 173848, "label": "yozlet"}, "created_at": "2020-12-31T22:32:25Z", "updated_at": "2020-12-31T22:32:25Z", "author_association": "NONE", "body": "Amazing work! And you've put in far more work than I'd expect to reduce the payload (which is admirable).\r\n\r\nSo, to add a plugin with the current design, it goes in (a) the template or (b) a bookmarklet, right?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 712260429, "label": "JavaScript plugin hooks mechanism similar to pluggy"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/983#issuecomment-752767500", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 752767500, "node_id": "MDEyOklzc3VlQ29tbWVudDc1Mjc2NzUwMA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-30T21:42:07Z", "updated_at": "2020-12-30T21:42:07Z", "author_association": "OWNER", "body": "Another option: have both \"dev\" and \"production\" versions of the plugin mechanism script. Make it easy to switch between the two. Build JavaScript unit tests that exercise the \"production\" APIs against the development version, and have extra tests that just work against the features in the development version.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 712260429, "label": "JavaScript plugin hooks mechanism similar to pluggy"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/983#issuecomment-753217714", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 753217714, "node_id": "MDEyOklzc3VlQ29tbWVudDc1MzIxNzcxNA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-31T22:21:33Z", "updated_at": "2020-12-31T22:21:33Z", "author_association": "OWNER", "body": "Eventually I'd like to provide a whole bunch of other `datasette.X` utility functions that plugins can use - things like `datasette.addTabbedContentPane()` or similar.\r\n\r\nBut I don't want to inline those into the page.\r\n\r\nSo... I think the basic plugin system remains inline - maybe from an inlined file called `plugins-bootstrap.js`. Then a separate `plugins.js` contains the rest of the API functionality.\r\n\r\nIf a plugin wants to take advantage of those APIs, maybe it registers itself using `datasette.plugins.register('load', () => ...)` - that `load` hook can then be fired once the bulkier plugin code has been loaded.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 712260429, "label": "JavaScript plugin hooks mechanism similar to pluggy"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/983#issuecomment-752770133", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 752770133, "node_id": "MDEyOklzc3VlQ29tbWVudDc1Mjc3MDEzMw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-30T21:53:45Z", "updated_at": "2020-12-30T21:54:22Z", "author_association": "OWNER", "body": "FixMyStreet inlines some JavaScript, and it's always a good idea to copy what they're doing when it comes to web performance: https://github.com/mysociety/fixmystreet/blob/23e9564b58a86b783ce47f3c0bf837cbd4fe7282/templates/web/base/common_header_tags.html#L19-L25\r\n\r\nNote `var fixmystreet=fixmystreet||{};` which is shorter - https://twitter.com/dracos/status/1344399909794045954", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 712260429, "label": "JavaScript plugin hooks mechanism similar to pluggy"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/983#issuecomment-753219521", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 753219521, "node_id": "MDEyOklzc3VlQ29tbWVudDc1MzIxOTUyMQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-31T22:39:52Z", "updated_at": "2020-12-31T22:39:52Z", "author_association": "OWNER", "body": "For inlining the `plugins.min.js` file into the Jinja templates I could use the trick described here: https://stackoverflow.com/a/41404611 - which adds a `{{ include_file('file.txt') }}` function to Jinja.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 712260429, "label": "JavaScript plugin hooks mechanism similar to pluggy"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/983#issuecomment-754210356", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 754210356, "node_id": "MDEyOklzc3VlQ29tbWVudDc1NDIxMDM1Ng==", "user": {"value": 222245, "label": "carlmjohnson"}, "created_at": "2021-01-04T20:49:05Z", "updated_at": "2021-01-04T20:49:05Z", "author_association": "NONE", "body": "For reasons [I've written about elsewhere](https://blog.carlmjohnson.net/post/2020/time-to-kill-ie11/), I'm in favor of modules. It has several beneficial effects. One, old browsers just ignore it all together. Two, if you include the same plain script on the page more than once, it will be executed twice, but if you include the same module script on a page twice, it will only execute once. Three, you get a module local namespace, instead of having to use the global window namespace or a function private namespace.\r\n\r\nOTOH, if you are going to use an old style script, the code from before isn't ideal, because you wipe out your registry if the script it included more than once. Also you may as well use object methods and splat arguments.\r\n\r\nThe event based architecture probably makes more sense though. Just make up some event names prefixed with `datasette:` and listen for them on the root. The only concern with that approach is it can sometimes be tricky to make sure your plugins are run after datasette has run. Maybe \r\n\r\n```js\r\nfunction mycallback(){\r\n // whatever\r\n}\r\n\r\nif (window.datasette) {\r\n window.datasette.init(mycallback);\r\n} else {\r\n document.addEventListener('datasette:init', mycallback);\r\n}\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 712260429, "label": "JavaScript plugin hooks mechanism similar to pluggy"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/983#issuecomment-752760054", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 752760054, "node_id": "MDEyOklzc3VlQ29tbWVudDc1Mjc2MDA1NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-30T21:12:36Z", "updated_at": "2020-12-30T21:14:05Z", "author_association": "OWNER", "body": "I gotta admit that 262 byte version is pretty tempting, if it's going to end up in the `` of every single page.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 712260429, "label": "JavaScript plugin hooks mechanism similar to pluggy"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/983#issuecomment-701629984", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 701629984, "node_id": "MDEyOklzc3VlQ29tbWVudDcwMTYyOTk4NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-09-30T20:34:43Z", "updated_at": "2020-09-30T20:34:43Z", "author_association": "OWNER", "body": "I had a look around and there isn't an obvious pluggy equivalent in JavaScript world at the moment. Lots of frameworks like jQuery and Vue have their own custom plugin mechanisms.\r\n\r\nhttps://github.com/rekit/js-plugin is a simple standalone plugin mechanism. Not quite as full-featured as Pluggy though - in particular I like how Pluggy supports multiple plugins returning results for the same hook that get concatenated into a list of results.\r\n\r\nhttps://css-tricks.com/designing-a-javascript-plugin-system/ has some ideas.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 712260429, "label": "JavaScript plugin hooks mechanism similar to pluggy"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/983#issuecomment-753224999", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 753224999, "node_id": "MDEyOklzc3VlQ29tbWVudDc1MzIyNDk5OQ==", "user": {"value": 11941245, "label": "jussiarpalahti"}, "created_at": "2020-12-31T23:29:36Z", "updated_at": "2020-12-31T23:29:36Z", "author_association": "NONE", "body": "I have yet to build Datasette plugin and am unfamiliar with Pluggy. Since browsers have event handling builtin Datasette could communicate with plugins through it. Handlers register as listeners for custom Datasette events and Datasette's JS can then trigger said events.\r\n\r\nI was also wondering if you had looked at Javascript Modules for JS plugins? With services like Skypack (https://www.skypack.dev) NPM libraries can be loaded directly into browser, no build step needed. Same goes for local JS if you adhere to ES Module spec. \r\n\r\nIf minification is required then tools such as Snowpack (https://www.snowpack.dev) could fit better. It uses https://github.com/evanw/esbuild for bundling and minification.\r\n\r\nOn plugins you'd simply:\r\n\r\n```javascript\r\nimport {register} from '/assets/js/datasette'\r\nregister.on({'click' : my_func})\r\n```\r\n\r\nIn Datasette HTML pages' head you'd merely import these files as modules one by one.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 712260429, "label": "JavaScript plugin hooks mechanism similar to pluggy"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/983#issuecomment-752747999", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 752747999, "node_id": "MDEyOklzc3VlQ29tbWVudDc1Mjc0Nzk5OQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-30T20:27:00Z", "updated_at": "2020-12-30T20:27:00Z", "author_association": "OWNER", "body": "I need to decide how this code is going to be loaded. Putting it in a blocking `