{"html_url": "https://github.com/simonw/datasette/issues/983#issuecomment-752773508", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 752773508, "node_id": "MDEyOklzc3VlQ29tbWVudDc1Mjc3MzUwOA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-30T22:10:08Z", "updated_at": "2020-12-30T22:11:34Z", "author_association": "OWNER", "body": "https://twitter.com/dracos/status/1344402639476424706 points out that plugins returning 0 will be ignored.\r\n\r\nThis should probably check for `result !== undefined` instead - knocks the size up to 250.", "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-752770488", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 752770488, "node_id": "MDEyOklzc3VlQ29tbWVudDc1Mjc3MDQ4OA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-30T21:55:35Z", "updated_at": "2020-12-30T21:58:26Z", "author_association": "OWNER", "body": "This one minifies to 241:\r\n```javascript\r\nvar datasette = datasette || {};\r\ndatasette.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 result = fn.apply(fn, parameters.map(parameter => args[parameter]));\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`var datasette=datasette||{};datasette.plugins=(()=>{var a={};return{register:(t,r,e)=>{a[t]||(a[t]=[]),a[t].push([r,e])},call:(t,r)=>{r=r||{};var e=[];return(a[t]||[]).forEach(([a,t])=>{var s=a.apply(a,t.map(a=>r[a]));s&&e.push(s)}),e}}})();`", "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-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-752767174", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 752767174, "node_id": "MDEyOklzc3VlQ29tbWVudDc1Mjc2NzE3NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-30T21:40:44Z", "updated_at": "2020-12-30T21:40:44Z", "author_association": "OWNER", "body": "Started a Twitter thread about this here: https://twitter.com/simonw/status/1344392603794477056", "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-752751490", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 752751490, "node_id": "MDEyOklzc3VlQ29tbWVudDc1Mjc1MTQ5MA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-30T20:40:04Z", "updated_at": "2020-12-30T21:34:22Z", "author_association": "OWNER", "body": "This one is 683 bytes with Uglify - I like how https://skalman.github.io/UglifyJS-online/ shows you the minified character count as you edit the script:\r\n```javascript\r\nwindow.datasette = window.datasette || {};\r\nwindow.datasette.plugins = (() => {\r\n var registry = {};\r\n var definitions = {};\r\n var stringify = JSON.stringify;\r\n\r\n function extractParameters(fn) {\r\n var match = /\\((.*)\\)/.exec(fn.toString());\r\n if (match && match[1].trim()) {\r\n return match[1].split(',').map(s => s.trim());\r\n } else {\r\n return [];\r\n }\r\n }\r\n\r\n function isSubSet(a, b) {\r\n return a.every(parameter => b.includes(parameter))\r\n }\r\n\r\n return {\r\n _r: registry,\r\n define: (hook, parameters) => {\r\n definitions[hook] = parameters || [];\r\n },\r\n register: (hook, fn, parameters) => {\r\n parameters = parameters || extractParameters(fn);\r\n if (!definitions[hook]) {\r\n throw 'Hook \"' + hook + '\" not defined';\r\n }\r\n /* Check parameters is a subset of definitions[hook] */\r\n var validParameters = definitions[hook];\r\n if (!isSubSet(parameters, validParameters)) {\r\n throw '\"' + hook + '\" valid args: ' + stringify(validParameters);\r\n }\r\n if (!registry[hook]) {\r\n registry[hook] = [];\r\n }\r\n registry[hook].push([fn, parameters]);\r\n },\r\n \r\n call: (hook, args) => {\r\n args = args || {};\r\n if (!definitions[hook]) {\r\n throw '\"' + hook + '\" hook not defined';\r\n }\r\n if (!isSubSet(Object.keys(args), definitions[hook])) {\r\n throw '\"' + hook + '\" valid args: ' + stringify(definitions[hook]);\r\n }\r\n \r\n var implementations = registry[hook] || [];\r\n var results = [];\r\n implementations.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 t={},r={},e=JSON.stringify;function i(t,r){return t.every(t=>r.includes(t))}return{_r:t,define:(t,e)=>{r[t]=e||[]},register:(a,n,o)=>{if(o=o||function(t){var r=/\\((.*)\\)/.exec(t.toString());return r&&r[1].trim()?r[1].split(\",\").map(t=>t.trim()):[]}(n),!r[a])throw'Hook \"'+a+'\" not defined';var d=r[a];if(!i(o,d))throw'\"'+a+'\" valid args: '+e(d);t[a]||(t[a]=[]),t[a].push([n,o])},call:(a,n)=>{if(n=n||{},!r[a])throw'\"'+a+'\" hook not defined';if(!i(Object.keys(n),r[a]))throw'\"'+a+'\" valid args: '+e(r[a]);var o=t[a]||[],d=[];return o.forEach(([t,r])=>{var e=r.map(t=>n[t]),i=t.apply(t,e);i&&d.push(i)}),d}}})();`", "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-752760815", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 752760815, "node_id": "MDEyOklzc3VlQ29tbWVudDc1Mjc2MDgxNQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-30T21:15:41Z", "updated_at": "2020-12-30T21:15:41Z", "author_association": "OWNER", "body": "I'm going to write a few example plugins and try them out against the longer and shorter versions of the script, to get a better feel for how useful the longer versions with the error handling and explicit definition actually are.", "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-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-752758802", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 752758802, "node_id": "MDEyOklzc3VlQ29tbWVudDc1Mjc1ODgwMg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-30T21:07:33Z", "updated_at": "2020-12-30T21:10:10Z", "author_association": "OWNER", "body": "Removing the `datasette.plugin.define()` method and associated error handling reduces the uglified version from 683 bytes to 380 bytes. I think the error checking is worth the extra 303 bytes per page load, even if it's only really needed for a better developer experience.\r\n```javascript\r\nwindow.datasette = window.datasette || {};\r\nwindow.datasette.plugins = (() => {\r\n var registry = {};\r\n\r\n function extractParameters(fn) {\r\n var match = /\\((.*)\\)/.exec(fn.toString());\r\n if (match && match[1].trim()) {\r\n return match[1].split(',').map(s => s.trim());\r\n } else {\r\n return [];\r\n }\r\n }\r\n return {\r\n register: (hook, fn, parameters) => {\r\n parameters = parameters || extractParameters(fn);\r\n if (!registry[hook]) {\r\n registry[hook] = [];\r\n }\r\n registry[hook].push([fn, parameters]);\r\n },\r\n \r\n call: (hook, args) => {\r\n args = args || {};\r\n var implementations = registry[hook] || [];\r\n var results = [];\r\n implementations.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 t={};return{register:(r,a,e)=>{e=e||function(t){var r=/\\((.*)\\)/.exec(t.toString());return r&&r[1].trim()?r[1].split(\",\").map(t=>t.trim()):[]}(a),t[r]||(t[r]=[]),t[r].push([a,e])},call:(r,a)=>{a=a||{};var e=t[r]||[],i=[];return e.forEach(([t,r])=>{var e=r.map(t=>a[t]),n=t.apply(t,e);n&&i.push(n)}),i}}})();`", "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-752757289", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 752757289, "node_id": "MDEyOklzc3VlQ29tbWVudDc1Mjc1NzI4OQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-30T21:02:20Z", "updated_at": "2020-12-30T21:02:20Z", "author_association": "OWNER", "body": "I'm going to need to add JavaScript unit tests for this new plugin system.", "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-752750551", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 752750551, "node_id": "MDEyOklzc3VlQ29tbWVudDc1Mjc1MDU1MQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-30T20:36:38Z", "updated_at": "2020-12-30T20:37:48Z", "author_association": "OWNER", "body": "This version minifies to 702 characters:\r\n```javascript\r\nwindow.datasette = window.datasette || {};\r\nwindow.datasette.plugins = (() => {\r\n var registry = {};\r\n var definitions = {};\r\n var stringify = JSON.stringify;\r\n\r\n function extractParameters(fn) {\r\n var match = /\\((.*)\\)/.exec(fn.toString());\r\n if (match && match[1].trim()) {\r\n return match[1].split(',').map(s => s.trim());\r\n } else {\r\n return [];\r\n }\r\n }\r\n\r\n function isSubSet(a, b) {\r\n return a.every(parameter => b.includes(parameter))\r\n }\r\n\r\n return {\r\n _registry: registry,\r\n define: (hook, parameters) => {\r\n definitions[hook] = parameters || [];\r\n },\r\n register: (hook, fn, parameters) => {\r\n parameters = parameters || extractParameters(fn);\r\n if (!definitions[hook]) {\r\n throw '\"' + hook + '\" is not a defined hook';\r\n }\r\n /* Check parameters is a subset of definitions[hook] */\r\n var validParameters = definitions[hook];\r\n if (!isSubSet(parameters, validParameters)) {\r\n throw '\"' + hook + '\" valid args are ' + stringify(validParameters);\r\n }\r\n if (!registry[hook]) {\r\n registry[hook] = [];\r\n }\r\n registry[hook].push([fn, parameters]);\r\n },\r\n \r\n call: (hook, args) => {\r\n args = args || {};\r\n if (!definitions[hook]) {\r\n throw '\"' + hook + '\" hook is not defined';\r\n }\r\n if (!isSubSet(Object.keys(args), definitions[hook])) {\r\n throw '\"' + hook + '\" valid args: ' + stringify(definitions[hook]);\r\n }\r\n \r\n var implementations = registry[hook] || [];\r\n var results = [];\r\n implementations.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\nOr 701 characters using https://skalman.github.io/UglifyJS-online/", "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-752749189", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 752749189, "node_id": "MDEyOklzc3VlQ29tbWVudDc1Mjc0OTE4OQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-30T20:31:28Z", "updated_at": "2020-12-30T20:31:28Z", "author_association": "OWNER", "body": "Using raw string exceptions, `throw '\"' + hook + '\" hook has not been defined';`, knocks it down to 795 characters.", "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-752748496", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/983", "id": 752748496, "node_id": "MDEyOklzc3VlQ29tbWVudDc1Mjc0ODQ5Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-12-30T20:28:48Z", "updated_at": "2020-12-30T20:28:48Z", "author_association": "OWNER", "body": "If I'm going to minify it I'll need to figure out a build step in Datasette itself so that I can easily work on that minified 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-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 `