home / github

Menu
  • GraphQL API

issue_comments

Table actions
  • GraphQL API for issue_comments

38 rows where "updated_at" is on date 2020-12-30

✎ View and edit SQL

This data as json, CSV (advanced)

Suggested facets: issue_url, issue, created_at (date), updated_at (date)

id ▼ html_url issue_url node_id user created_at updated_at author_association body reactions issue performed_via_github_app
752696499 https://github.com/simonw/datasette/issues/987#issuecomment-752696499 https://api.github.com/repos/simonw/datasette/issues/987 MDEyOklzc3VlQ29tbWVudDc1MjY5NjQ5OQ== simonw 9599 2020-12-30T17:21:08Z 2020-12-30T17:21:08Z OWNER More generally, I need to document certain areas of the page that JavaScript plugins are invited to append their content to - such that plugin authors can use them and feel confident that future changes to the Datasette templates won't break their plugins. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} Documented HTML hooks for JavaScript plugin authors 712984738  
752697279 https://github.com/simonw/datasette/issues/987#issuecomment-752697279 https://api.github.com/repos/simonw/datasette/issues/987 MDEyOklzc3VlQ29tbWVudDc1MjY5NzI3OQ== simonw 9599 2020-12-30T17:23:27Z 2020-12-30T17:23:32Z OWNER Related problem: right now if you're writing custom template it's not at all obvious how to write them such that visualization plugins like `datasette-cluster-map` will continue to work. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} Documented HTML hooks for JavaScript plugin authors 712984738  
752714747 https://github.com/simonw/datasette/issues/987#issuecomment-752714747 https://api.github.com/repos/simonw/datasette/issues/987 MDEyOklzc3VlQ29tbWVudDc1MjcxNDc0Nw== simonw 9599 2020-12-30T18:23:08Z 2020-12-30T18:23:20Z OWNER In terms of "places to put your plugin content", the simplest solution I can think of is something like this: ```html <div id="plugin-content-pre-table"></div> ``` Alternative designs: - A documented JavaScript function that returns the CSS selector where plugins should put their content - A documented JavaScript function that returns a DOM node where plugins should put their content. This would allow the JavaScript to create the element if it does not already exist (though it wouldn't be obvious WHERE that element should be created) - Documented JavaScript functions for things like "append this node/HTML to the place-where-plugins-go" I think the original option - an empty `<div>` with a known `id` attribute - is the right one to go with here. It's the simplest, it's very easy for custom template authors to understand and it acknowledges that plugins may have all kinds of extra crazy stuff they want to do - like checking in that div to see if another plugin has written to it already, for example. {"total_count": 1, "+1": 1, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} Documented HTML hooks for JavaScript plugin authors 712984738  
752715236 https://github.com/simonw/datasette/issues/983#issuecomment-752715236 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1MjcxNTIzNg== simonw 9599 2020-12-30T18:24:54Z 2020-12-30T18:24:54Z OWNER I think I'm going to try building a very lightweight clone of the core API design of Pluggy - not the advanced features, just the idea that plugins can register and a call to `plugin.nameOfHook()` will return the concatenated results of all of the registered hooks. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752715412 https://github.com/simonw/datasette/issues/983#issuecomment-752715412 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1MjcxNTQxMg== simonw 9599 2020-12-30T18:25:31Z 2020-12-30T18:25:31Z OWNER I'm going to introduce a global `datasette` object which holds all the documented JavaScript API for plugin authors. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752721069 https://github.com/simonw/datasette/issues/983#issuecomment-752721069 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1MjcyMTA2OQ== simonw 9599 2020-12-30T18:46:10Z 2020-12-30T18:46:10Z OWNER Pluggy does dependency injection by introspecting the named arguments to the Python function, which I really like. That's tricker in JavaScript. It looks like the only way to introspect a function is to look at the `.toString()` representation of it and parse the `(parameter, list)` using a regular expression. Even more challenging: JavaScript developers love minifying their code, and minification can shorten the function parameter names. From https://code-maven.com/dependency-injection-in-angularjs it looks like Angular.js does dependency injection and solves this by letting you optionally provide a separate list of the arguments your function uses: ```javascript angular.module('DemoApp', []) .controller('DemoController', ['$scope', '$log', function($scope, $log) { $scope.message = "Hello World"; $log.debug('logging hello'); }]); ``` I can copy that approach: I'll introspect by default, but provide a documented mechanism for explicitly listing your parameter names so that if you know your plugin code will be minified you can use that instead. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752721840 https://github.com/simonw/datasette/issues/983#issuecomment-752721840 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1MjcyMTg0MA== simonw 9599 2020-12-30T18:48:53Z 2020-12-30T18:51:51Z OWNER Potential design: ```javascript datasette.plugins.register('column_actions', function(database, table, column, actor) { /* ... *l }) ``` Or if you want to be explicit to survive minification: ```javascript datasette.plugins.register('column_actions', function(database, table, column, actor) { /* ... *l }, ['database', 'table', 'column', 'actor']) ``` I'm making that list of parameter names an optional third argument to the `register()` function. If that argument isn't passed, introspection will be used to figure out the parameter names. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752722863 https://github.com/simonw/datasette/issues/983#issuecomment-752722863 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1MjcyMjg2Mw== simonw 9599 2020-12-30T18:52:39Z 2020-12-30T18:52:39Z OWNER Then to call the plugins: ```javascript datasette.plugins.call('column_actions', {database: 'database', table: 'table'}) ``` {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752729035 https://github.com/simonw/datasette/issues/983#issuecomment-752729035 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1MjcyOTAzNQ== simonw 9599 2020-12-30T19:15:56Z 2020-12-30T19:16:44Z OWNER The `column_actions` hook is the obvious first place to try this out. What are some demo plugins I could build for it? - Word cloud for this column - Count values (essentially linking to the SQL query for that column, as an extended version of the facet counts) - would be great if this could include pagination somehow, via #856. - Extract this column into a separate table - Add an index to this column {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752742669 https://github.com/simonw/datasette/issues/983#issuecomment-752742669 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1Mjc0MjY2OQ== simonw 9599 2020-12-30T20:07:05Z 2020-12-30T20:07:18Z OWNER Initial prototype: ```javascript window.datasette = {}; window.datasette.plugins = (function() { var registry = {}; function extractParameters(fn) { var match = /\((.*)\)/.exec(fn.toString()); if (match && match[1].trim()) { return match[1].split(',').map(s => s.trim()); } else { return []; } } function register(hook, fn, parameters) { parameters = parameters || extractParameters(fn); if (!registry[hook]) { registry[hook] = []; } registry[hook].push([fn, parameters]); } function call(hook, args) { args = args || {}; var implementations = registry[hook] || []; var results = []; implementations.forEach(([fn, parameters]) => { /* Call with the correct arguments */ var callWith = parameters.map(parameter => args[parameter]); var result = fn.apply(fn, callWith); if (result) { results.push(result); } }); return results; } return { register: register, _registry: registry, call: call }; })(); ``` Usage example: ```javascript datasette.plugins.register('numbers', (a, b) => a + b) datasette.plugins.register('numbers', (a, b) => a * b) datasette.plugins.call('numbers', {a: 4, b: 6}) /* Returns [10, 24] */ ``` {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752744195 https://github.com/simonw/datasette/issues/983#issuecomment-752744195 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1Mjc0NDE5NQ== simonw 9599 2020-12-30T20:12:26Z 2020-12-30T20:12:26Z OWNER This implementation doesn't have an equivalent of "hookspecs" which can identify if a registered plugin implementation matches a known signature. I should add that, it will provide a better developer experience if someone has a typo. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752744311 https://github.com/simonw/datasette/issues/983#issuecomment-752744311 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1Mjc0NDMxMQ== simonw 9599 2020-12-30T20:12:50Z 2020-12-30T20:13:02Z OWNER This could work to define a plugin hook: ```javascript datasette.plugins.define('numbers', ['a' ,'b']) ``` {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752747169 https://github.com/simonw/datasette/issues/983#issuecomment-752747169 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1Mjc0NzE2OQ== simonw 9599 2020-12-30T20:24:07Z 2020-12-30T20:24:07Z OWNER This version adds `datasette.plugins.define()` plus extra validation of both `.register()` and `.call()`: ```javascript window.datasette = {}; window.datasette.plugins = (function() { var registry = {}; var definitions = {}; function extractParameters(fn) { var match = /\((.*)\)/.exec(fn.toString()); if (match && match[1].trim()) { return match[1].split(',').map(s => s.trim()); } else { return []; } } function define(hook, parameters) { definitions[hook] = parameters || []; } function isSubSet(a, b) { return a.every(parameter => b.includes(parameter)) } function register(hook, fn, parameters) { parameters = parameters || extractParameters(fn); if (!definitions[hook]) { throw new Error('"' + hook + '" is not a defined plugin hook'); } if (!definitions[hook]) { throw new Error('"' + hook + '" is not a defined plugin hook'); } /* Check parameters is a subset of definitions[hook] */ var validParameters = definitions[hook]; if (!isSubSet(parameters, validParameters)) { throw new Error('"' + hook + '" valid parameters are ' + JSON.stringify(validParameters)); } if (!registry[hook]) { registry[hook] = []; } registry[hook].push([fn, parameters]); } function call(hook, args) { args = args || {}; if (!definitions[hook]) { throw new Error('"' + hook + '" hook has not been defined'); } if (!isSubSet(Object.keys(args), definitions[hook])) { throw new Error('"' + hook + '" valid arguments are ' + JSON.stringify(definitions[hook])); } var implementations = registry[hook] || []; var results = []; implementations.forEach(([fn, parameters]) => { /* Call with the correct arguments */ var callWith =… {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752747999 https://github.com/simonw/datasette/issues/983#issuecomment-752747999 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1Mjc0Nzk5OQ== simonw 9599 2020-12-30T20:27:00Z 2020-12-30T20:27:00Z OWNER I need to decide how this code is going to be loaded. Putting it in a blocking `<script>` element in the head would work, but I'd rather not block loading of the rest of the page. Using a `<script async>` method would be nicer, but then I have to worry about plugins attempting to register themselves before the page has fully loaded. Running it through https://javascript-minifier.com/ produces this, which is 855 characters - so maybe I could inline that into the header of the page? `window.datasette={},window.datasette.plugins=function(){var r={},n={};function e(r,n){return r.every(r=>n.includes(r))}return{define:function(r,e){n[r]=e||[]},register:function(t,i,o){if(o=o||function(r){var n=/\((.*)\)/.exec(r.toString());return n&&n[1].trim()?n[1].split(",").map(r=>r.trim()):[]}(i),!n[t])throw new Error('"'+t+'" is not a defined plugin hook');if(!n[t])throw new Error('"'+t+'" is not a defined plugin hook');var a=n[t];if(!e(o,a))throw new Error('"'+t+'" valid parameters are '+JSON.stringify(a));r[t]||(r[t]=[]),r[t].push([i,o])},_registry:r,call:function(t,i){if(i=i||{},!n[t])throw new Error('"'+t+'" hook has not been defined');if(!e(Object.keys(i),n[t]))throw new Error('"'+t+'" valid arguments are '+JSON.stringify(n[t]));var o=r[t]||[],a=[];return o.forEach(([r,n])=>{var e=n.map(r=>i[r]),t=r.apply(r,e);t&&a.push(t)}),a}}}();` {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752748496 https://github.com/simonw/datasette/issues/983#issuecomment-752748496 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1Mjc0ODQ5Ng== simonw 9599 2020-12-30T20:28:48Z 2020-12-30T20:28:48Z OWNER 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. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752749189 https://github.com/simonw/datasette/issues/983#issuecomment-752749189 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1Mjc0OTE4OQ== simonw 9599 2020-12-30T20:31:28Z 2020-12-30T20:31:28Z OWNER Using raw string exceptions, `throw '"' + hook + '" hook has not been defined';`, knocks it down to 795 characters. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752750551 https://github.com/simonw/datasette/issues/983#issuecomment-752750551 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1Mjc1MDU1MQ== simonw 9599 2020-12-30T20:36:38Z 2020-12-30T20:37:48Z OWNER This version minifies to 702 characters: ```javascript window.datasette = window.datasette || {}; window.datasette.plugins = (() => { var registry = {}; var definitions = {}; var stringify = JSON.stringify; function extractParameters(fn) { var match = /\((.*)\)/.exec(fn.toString()); if (match && match[1].trim()) { return match[1].split(',').map(s => s.trim()); } else { return []; } } function isSubSet(a, b) { return a.every(parameter => b.includes(parameter)) } return { _registry: registry, define: (hook, parameters) => { definitions[hook] = parameters || []; }, register: (hook, fn, parameters) => { parameters = parameters || extractParameters(fn); if (!definitions[hook]) { throw '"' + hook + '" is not a defined hook'; } /* Check parameters is a subset of definitions[hook] */ var validParameters = definitions[hook]; if (!isSubSet(parameters, validParameters)) { throw '"' + hook + '" valid args are ' + stringify(validParameters); } if (!registry[hook]) { registry[hook] = []; } registry[hook].push([fn, parameters]); }, call: (hook, args) => { args = args || {}; if (!definitions[hook]) { throw '"' + hook + '" hook is not defined'; } if (!isSubSet(Object.keys(args), definitions[hook])) { throw '"' + hook + '" valid args: ' + stringify(definitions[hook]); } var implementations = registry[hook] || []; var results = []; implementations.forEach(([fn, parameters]) => { /* Call with the correct arguments */ var callWith = parameters.map(parameter => args[parameter]); … {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752751490 https://github.com/simonw/datasette/issues/983#issuecomment-752751490 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1Mjc1MTQ5MA== simonw 9599 2020-12-30T20:40:04Z 2020-12-30T21:34:22Z OWNER 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: ```javascript window.datasette = window.datasette || {}; window.datasette.plugins = (() => { var registry = {}; var definitions = {}; var stringify = JSON.stringify; function extractParameters(fn) { var match = /\((.*)\)/.exec(fn.toString()); if (match && match[1].trim()) { return match[1].split(',').map(s => s.trim()); } else { return []; } } function isSubSet(a, b) { return a.every(parameter => b.includes(parameter)) } return { _r: registry, define: (hook, parameters) => { definitions[hook] = parameters || []; }, register: (hook, fn, parameters) => { parameters = parameters || extractParameters(fn); if (!definitions[hook]) { throw 'Hook "' + hook + '" not defined'; } /* Check parameters is a subset of definitions[hook] */ var validParameters = definitions[hook]; if (!isSubSet(parameters, validParameters)) { throw '"' + hook + '" valid args: ' + stringify(validParameters); } if (!registry[hook]) { registry[hook] = []; } registry[hook].push([fn, parameters]); }, call: (hook, args) => { args = args || {}; if (!definitions[hook]) { throw '"' + hook + '" hook not defined'; } if (!isSubSet(Object.keys(args), definitions[hook])) { throw '"' + hook + '" valid args: ' + stringify(definitions[hook]); } var implementations = registry[hook] || []; var results = []; implementations.forEach(([fn, parameters]) => { /* Call with the correct… {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752756612 https://github.com/simonw/datasette/issues/1164#issuecomment-752756612 https://api.github.com/repos/simonw/datasette/issues/1164 MDEyOklzc3VlQ29tbWVudDc1Mjc1NjYxMg== simonw 9599 2020-12-30T20:59:54Z 2020-12-30T20:59:54Z OWNER I tried a few different pure-Python JavaScript minifying libraries and none of them produced results as good as https://www.npmjs.com/package/uglify-js for the plugin code I'm considering in #983. So I think I'll need to rely on a Node.js tool for this. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} Mechanism for minifying JavaScript that ships with Datasette 776634318  
752757075 https://github.com/simonw/datasette/issues/1164#issuecomment-752757075 https://api.github.com/repos/simonw/datasette/issues/1164 MDEyOklzc3VlQ29tbWVudDc1Mjc1NzA3NQ== simonw 9599 2020-12-30T21:01:27Z 2020-12-30T21:01:27Z OWNER I don't want Datasette contributors to need a working Node.js install to run the tests or work on Datasette unless they are explicitly working on the JavaScript. I think I'm going to do this with a unit test that runs only if `upglify-js` is available on the path and confirms that the `*.min.js` version of each script in the repository correctly matches the results from running `uglify-js` against it. That way if anyone checks in a change to JavaScript but forgets to run the minifier the tests will fail in CI. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} Mechanism for minifying JavaScript that ships with Datasette 776634318  
752757289 https://github.com/simonw/datasette/issues/983#issuecomment-752757289 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1Mjc1NzI4OQ== simonw 9599 2020-12-30T21:02:20Z 2020-12-30T21:02:20Z OWNER I'm going to need to add JavaScript unit tests for this new plugin system. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752757910 https://github.com/simonw/datasette/issues/1165#issuecomment-752757910 https://api.github.com/repos/simonw/datasette/issues/1165 MDEyOklzc3VlQ29tbWVudDc1Mjc1NzkxMA== simonw 9599 2020-12-30T21:04:18Z 2020-12-30T21:04:18Z OWNER https://jestjs.io/ looks worth trying here. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} Mechanism for executing JavaScript unit tests 776635426  
752758802 https://github.com/simonw/datasette/issues/983#issuecomment-752758802 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1Mjc1ODgwMg== simonw 9599 2020-12-30T21:07:33Z 2020-12-30T21:10:10Z OWNER 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. ```javascript window.datasette = window.datasette || {}; window.datasette.plugins = (() => { var registry = {}; function extractParameters(fn) { var match = /\((.*)\)/.exec(fn.toString()); if (match && match[1].trim()) { return match[1].split(',').map(s => s.trim()); } else { return []; } } return { register: (hook, fn, parameters) => { parameters = parameters || extractParameters(fn); if (!registry[hook]) { registry[hook] = []; } registry[hook].push([fn, parameters]); }, call: (hook, args) => { args = args || {}; var implementations = registry[hook] || []; var results = []; implementations.forEach(([fn, parameters]) => { /* Call with the correct arguments */ var callWith = parameters.map(parameter => args[parameter]); var result = fn.apply(fn, callWith); if (result) { results.push(result); } }); return results; } }; })(); ``` `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}}})();` {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752759885 https://github.com/simonw/datasette/issues/983#issuecomment-752759885 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1Mjc1OTg4NQ== simonw 9599 2020-12-30T21:11:52Z 2020-12-30T21:14:00Z OWNER 262 bytes if I remove the parameter introspection code, instead requiring plugin authors to specify the arguments they take: ```javascript window.datasette = window.datasette || {}; window.datasette.plugins = (() => { var registry = {}; return { register: (hook, fn, parameters) => { if (!registry[hook]) { registry[hook] = []; } registry[hook].push([fn, parameters]); }, call: (hook, args) => { args = args || {}; var results = []; (registry[hook] || []).forEach(([fn, parameters]) => { /* Call with the correct arguments */ var callWith = parameters.map(parameter => args[parameter]); var result = fn.apply(fn, callWith); if (result) { results.push(result); } }); return results; } }; })(); ``` `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}}})();` {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752760054 https://github.com/simonw/datasette/issues/983#issuecomment-752760054 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1Mjc2MDA1NA== simonw 9599 2020-12-30T21:12:36Z 2020-12-30T21:14:05Z OWNER I gotta admit that 262 byte version is pretty tempting, if it's going to end up in the `<head>` of every single page. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752760815 https://github.com/simonw/datasette/issues/983#issuecomment-752760815 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1Mjc2MDgxNQ== simonw 9599 2020-12-30T21:15:41Z 2020-12-30T21:15:41Z OWNER 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. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752767174 https://github.com/simonw/datasette/issues/983#issuecomment-752767174 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1Mjc2NzE3NA== simonw 9599 2020-12-30T21:40:44Z 2020-12-30T21:40:44Z OWNER Started a Twitter thread about this here: https://twitter.com/simonw/status/1344392603794477056 {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752767500 https://github.com/simonw/datasette/issues/983#issuecomment-752767500 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1Mjc2NzUwMA== simonw 9599 2020-12-30T21:42:07Z 2020-12-30T21:42:07Z OWNER 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. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752768652 https://github.com/simonw/datasette/issues/1164#issuecomment-752768652 https://api.github.com/repos/simonw/datasette/issues/1164 MDEyOklzc3VlQ29tbWVudDc1Mjc2ODY1Mg== simonw 9599 2020-12-30T21:46:29Z 2020-12-30T21:46:29Z OWNER Running https://skalman.github.io/UglifyJS-online/ against https://github.com/simonw/datasette/blob/0.53/datasette/static/table.js knocks it down from 7810 characters to 4643. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} Mechanism for minifying JavaScript that ships with Datasette 776634318  
752768785 https://github.com/simonw/datasette/issues/1164#issuecomment-752768785 https://api.github.com/repos/simonw/datasette/issues/1164 MDEyOklzc3VlQ29tbWVudDc1Mjc2ODc4NQ== simonw 9599 2020-12-30T21:47:06Z 2020-12-30T21:47:06Z OWNER If I'm going to minify `table.js` I'd like to offer a source map for it. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} Mechanism for minifying JavaScript that ships with Datasette 776634318  
752769452 https://github.com/simonw/datasette/issues/1164#issuecomment-752769452 https://api.github.com/repos/simonw/datasette/issues/1164 MDEyOklzc3VlQ29tbWVudDc1Mjc2OTQ1Mg== simonw 9599 2020-12-30T21:50:16Z 2020-12-30T21:50:16Z OWNER If I implement this I can automate the CodeMirror minification and remove the bit about running `uglify-js` against it from the documentation here: https://docs.datasette.io/en/0.53/contributing.html#upgrading-codemirror {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} Mechanism for minifying JavaScript that ships with Datasette 776634318  
752770133 https://github.com/simonw/datasette/issues/983#issuecomment-752770133 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1Mjc3MDEzMw== simonw 9599 2020-12-30T21:53:45Z 2020-12-30T21:54:22Z OWNER 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 Note `var fixmystreet=fixmystreet||{};` which is shorter - https://twitter.com/dracos/status/1344399909794045954 {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752770488 https://github.com/simonw/datasette/issues/983#issuecomment-752770488 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1Mjc3MDQ4OA== simonw 9599 2020-12-30T21:55:35Z 2020-12-30T21:58:26Z OWNER This one minifies to 241: ```javascript var datasette = datasette || {}; datasette.plugins = (() => { var registry = {}; return { register: (hook, fn, parameters) => { if (!registry[hook]) { registry[hook] = []; } registry[hook].push([fn, parameters]); }, call: (hook, args) => { args = args || {}; var results = []; (registry[hook] || []).forEach(([fn, parameters]) => { /* Call with the correct arguments */ var result = fn.apply(fn, parameters.map(parameter => args[parameter])); if (result) { results.push(result); } }); return results; } }; })(); ``` `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}}})();` {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752773508 https://github.com/simonw/datasette/issues/983#issuecomment-752773508 https://api.github.com/repos/simonw/datasette/issues/983 MDEyOklzc3VlQ29tbWVudDc1Mjc3MzUwOA== simonw 9599 2020-12-30T22:10:08Z 2020-12-30T22:11:34Z OWNER https://twitter.com/dracos/status/1344402639476424706 points out that plugins returning 0 will be ignored. This should probably check for `result !== undefined` instead - knocks the size up to 250. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} JavaScript plugin hooks mechanism similar to pluggy 712260429  
752777744 https://github.com/simonw/datasette/issues/1165#issuecomment-752777744 https://api.github.com/repos/simonw/datasette/issues/1165 MDEyOklzc3VlQ29tbWVudDc1Mjc3Nzc0NA== simonw 9599 2020-12-30T22:30:24Z 2020-12-30T22:30:24Z OWNER https://www.valentinog.com/blog/jest/ was useful. I created a `static/__tests__` folder and added this file as `plugins.spec.js`: ```javascript const datasette = require("../plugins.js"); describe("Datasette Plugins", () => { test("it should have datasette.plugins", () => { expect(!!datasette.plugins).toEqual(true); }); test("registering a plugin should work", () => { datasette.plugins.register("numbers", (a, b) => a + b, ["a", "b"]); var result = datasette.plugins.call("numbers", { a: 1, b: 2 }); expect(result).toEqual([3]); datasette.plugins.register("numbers", (a, b) => a * b, ["a", "b"]); var result2 = datasette.plugins.call("numbers", { a: 1, b: 2 }); expect(result2).toEqual([3, 2]); }); }); ``` In `static/plugins.js` I put this: ```javascript var datasette = datasette || {}; datasette.plugins = (() => { var registry = {}; return { register: (hook, fn, parameters) => { if (!registry[hook]) { registry[hook] = []; } registry[hook].push([fn, parameters]); }, call: (hook, args) => { args = args || {}; var results = []; (registry[hook] || []).forEach(([fn, parameters]) => { /* Call with the correct arguments */ var result = fn.apply(fn, parameters.map(parameter => args[parameter])); if (result !== undefined) { results.push(result); } }); return results; } }; })(); module.exports = datasette; ``` Note the `module.exports` line at the end. Then inside `static/` I ran the following command: ``` % npx jest -c '{}' PASS __tests__/plugins.spec.js Datasette Plugins ✓ it should have datasette.plugins (3 ms) ✓ registering a plugin should work (1 ms) Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 0 total Time: 1.163 s Ran all test su… {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} Mechanism for executing JavaScript unit tests 776635426  
752779490 https://github.com/simonw/datasette/issues/1165#issuecomment-752779490 https://api.github.com/repos/simonw/datasette/issues/1165 MDEyOklzc3VlQ29tbWVudDc1Mjc3OTQ5MA== simonw 9599 2020-12-30T22:38:43Z 2020-12-30T22:38:43Z OWNER Turned that into a TIL: https://til.simonwillison.net/javascript/jest-without-package-json {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} Mechanism for executing JavaScript unit tests 776635426  
752779820 https://github.com/simonw/datasette/issues/1165#issuecomment-752779820 https://api.github.com/repos/simonw/datasette/issues/1165 MDEyOklzc3VlQ29tbWVudDc1Mjc3OTgyMA== simonw 9599 2020-12-30T22:40:28Z 2020-12-30T22:40:28Z OWNER I don't know if Jest on the command-line is the right tool for this. It works for the `plugins.js` script but I'm increasingly going to want to start adding tests for browser JavaScript features - like the https://github.com/simonw/datasette/blob/0.53/datasette/static/table.js script - which will need to run in a browser. So maybe I should just find a browser testing solution and figure out how to run that under CI in GitHub Actions. Maybe https://www.cypress.io/ ? {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} Mechanism for executing JavaScript unit tests 776635426  
752780000 https://github.com/simonw/datasette/issues/1165#issuecomment-752780000 https://api.github.com/repos/simonw/datasette/issues/1165 MDEyOklzc3VlQ29tbWVudDc1Mjc4MDAwMA== simonw 9599 2020-12-30T22:41:25Z 2020-12-30T22:41:25Z OWNER Jest works with Puppeteer: https://jestjs.io/docs/en/puppeteer {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} Mechanism for executing JavaScript unit tests 776635426  

Advanced export

JSON shape: default, array, newline-delimited, object

CSV options:

CREATE TABLE [issue_comments] (
   [html_url] TEXT,
   [issue_url] TEXT,
   [id] INTEGER PRIMARY KEY,
   [node_id] TEXT,
   [user] INTEGER REFERENCES [users]([id]),
   [created_at] TEXT,
   [updated_at] TEXT,
   [author_association] TEXT,
   [body] TEXT,
   [reactions] TEXT,
   [issue] INTEGER REFERENCES [issues]([id])
, [performed_via_github_app] TEXT);
CREATE INDEX [idx_issue_comments_issue]
                ON [issue_comments] ([issue]);
CREATE INDEX [idx_issue_comments_user]
                ON [issue_comments] ([user]);
Powered by Datasette · Queries took 490.615ms · About: simonw/datasette-graphql