Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin: Implement block registering API #289

Merged
merged 24 commits into from
Mar 22, 2017

Conversation

youknowriad
Copy link
Contributor

@youknowriad youknowriad commented Mar 17, 2017

Questions

  • I was thinking about adding unit tests for these fonctions, but setting up QUnit is not an easy task with our setup. Do you think we could use mocha maybe?
  • Also: Using a library like lodash here seems appropriate, what are your thoughts on adding it?
  • We're using a tuple (namespace, name) to identify a block, should we instead merge them into one id ( namespace/name )?

@youknowriad youknowriad added the Framework Issues related to broader framework topics, especially as it relates to javascript label Mar 17, 2017
@youknowriad youknowriad self-assigned this Mar 17, 2017
@youknowriad youknowriad requested a review from aduth March 17, 2017 13:07
@youknowriad youknowriad force-pushed the update/implement-block-registering branch 2 times, most recently from b445c38 to 2694251 Compare March 17, 2017 13:14
}

/**
* Returns all registered blocks.
*
* @return {Object} Block settings keyed by block name
* @return {Array} Blocks settings
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not keyed by block name because the block name is not unique, it's unique inside a namespace only.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm worried we'll find the namespacing will get in our way more than it helps. I'm not totally convinced about it. It's nice to have the consistency with similar APIs for the REST API, but I wonder if we could just allow a simple name and perhaps encourage a convention around including some namespace in the name itself: like "wp/text" (or without namespace for "core" types).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the expected uses of this function? Do you think we will need the ability to pull a specific block out of the list? If so, it might be a good idea to return an array keyed by namespace then block name, as the blocks are stored.

Also, Block settings is better wording.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the expected uses of this function?

This is a good point. Seems nice as an external API, but I don't see an obvious use-case for us initially.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my experience with the multi-instance prototype. This function was used in two places:

  • Fetching all the blocks to show in the block inserter (Array is better here, because we need to group them by category and not by namespace)

  • Fetching all the blocks to find the blocks that a block can be switched to (The filtering happens using one of the settings key and not the namespace).

So IMO it's better to return a simple array.

Regarding the name, I'm leaning towards removing the namespace and encouraging a convention

Copy link
Member

@nylen nylen Mar 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the name, I'm leaning towards removing the namespace and encouraging a convention

Encouraging a convention is not enough, it needs to be enforced otherwise people won't follow it.

Though this can be done either way -

if ( ! /^\w+/\w+$/\.test( blockName ) ) {
    throw new Error( 'Use a namespace please' );
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repeating my comment elsewhere, I think we can let go of namespace. The name should be unique and descriptive anyways because the user would interact and search for it.


blocks[ namespace ][ block ] = Object.assign(
{ name: block, namespace },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we call it name on the object, should we just accept the argument labeled as name as well?

@@ -1,5 +1,7 @@
export { default as Editable } from './components/editable';

const blocks = {};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be nice to provide a DocBlock for this.

@nylen
Copy link
Member

nylen commented Mar 17, 2017

I was thinking about adding unit tests for these functions [...] Do you think we could use mocha maybe?

Yes, let's go ahead and do this please :) Whatever we choose should support browser testing as well.

I agree I like Mocha better, but core uses QUnit. Do you see this causing problems in the future if we need to integrate both? What are the difficulties in adding QUnit?

Also: Using a library like lodash here seems appropriate, what are your thoughts on adding it?

+1. WP core already bundles Underscore, though Lodash is nicer to work with and we would be able to include selective imports in a build process.

We're using a tuple (namespace, name) to identify a block, should we instead merge them into one id ( namespace/name )?

If we do keep the namespace and name separate, this makes it easier to test and enforce guidelines for block authors using these fields correctly.

@youknowriad
Copy link
Contributor Author

If we do keep the namespace and name separate

The problem with keeping the name and namespace separate is that each time we'll need to retrieve the block settings we'll do getBlockSettings( blockState.name, blockState.namespace) which means we need to store a namespace and a name in the block state and in the generated markup (or metadata). <!-- wp:text namespace:core --> while we could keep it simpler <!-- wp:core/text -->

@nylen
Copy link
Member

nylen commented Mar 17, 2017

getBlockSettings( blockState.name, blockState.namespace )

Good point, that is not ideal :)

As long as we can do something like #289 (comment), then combining the namespace and name makes sense to me.

@youknowriad
Copy link
Contributor Author

youknowriad commented Mar 17, 2017

I updated the API and dropped the namespace.

Do you see this causing problems in the future if we need to integrate both? What are the difficulties in adding QUnit?

My experience with QUnit is equal to Zero :). So after reading the docs, I figured out that integrating it with our build system means having a separate webpack.config for the tests and import all the test files on a test entry point file. Not so handy IMO, but maybe I'm missing a better way to setup qunit. If someone could help on setting up QUnit (could be a separate PR), it would be awesome!!!

@nylen
Copy link
Member

nylen commented Mar 17, 2017

I would very much prefer that we start now with the rule of "do not merge code unless it has tests".

Also, mocha is going to be much nicer to work with than QUnit, and thinking about it a bit more, any difficulties with integration are likely to be pretty minor. I'll try to add it here.

@nylen nylen force-pushed the update/implement-block-registering branch 2 times, most recently from 1e4b7b2 to c2e4c8c Compare March 17, 2017 17:13
@nylen
Copy link
Member

nylen commented Mar 17, 2017

@mtias I'm replying to your #289 (comment) here because the thread has disappeared after a rebase.

Repeating my comment elsewhere, I think we can let go of namespace. The name should be unique and descriptive anyways because the user would interact and search for it.

I am not sure whether you mean letting go of namespace as a separate argument, or the concept entirely. I agree that using the namespace as a separate argument is going to lead to more complicated code, removing it in 65d60a6 has already simplified things a good bit.

But we really need to require that blocks use a plugin-specific prefix for their internal ID/name/slug/whatever you call it, and in general we need to start thinking about how blocks can be named and how their resulting <!-- wp --> tags will look. Otherwise there will be collisions and it will be unpleasant.

There is a lot of precedent in WP core for doing similar things. The reasoning is laid out really well in this blog post: https://nacin.com/2010/05/11/in-wordpress-prefix-everything/

Here are a couple more specific examples:

IMO, it's also worth considering whether we need to version blocks similarly to REST API endpoints. This is something we could add later on if we need it, though.

@mtias
Copy link
Member

mtias commented Mar 17, 2017

@nylen yes, sorry for the confusion, I mean using specific argument or / syntax for namespacing and just doing myplugin-map or myplugin_map.

@nylen
Copy link
Member

nylen commented Mar 17, 2017

I'm not planning to spend a lot of time on it, beyond making it work and writing some simple tests, but enforcing a very specific syntax is a good thing. We can always relax the rules later when more needs become apparent, but we can't do the reverse when people misuse the functionality provided.

*/
export function getBlocks() {

return Object.values( blocks );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will probably require pulling in babel-plugin-transform-runtime, otherwise will error on browsers where this is not yet implemented.

.babelrc Outdated
@@ -1,9 +1,8 @@
{
"presets": [
[ "latest", {
"es2015": {
"modules": false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was important to have in order to achieve Webpack tree shaking.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, interesting. At one point I was getting an error until I removed this (http://stackoverflow.com/a/42834190) but now it is working fine.

.babelrc Outdated
"modules": false
}
} ]
[ "latest", "es2015" ]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, es2015 is included in latest, I think it's not needed here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a further change we should make here? I'm not sure if you can do latest: { modules: false } for example, or how to test whether this is working as intended.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the last change is good 👍

Yes, hard to know if it's working correctly. I guess we'll see when we import some function for lodash. We could check that it's not including the whole library

@nylen
Copy link
Member

nylen commented Mar 17, 2017

This now has mocha unit tests 🎉

A few notes about the test setup:

import chai, { expect } from 'chai';
import dirtyChai from 'dirty-chai';

import * as blocks from '@wordpress/blocks';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why you would use * instead of explicit imports?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no preference either way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this particular case, it's not making any difference, but worth noting that in other use cases when importing from external libraries, it can have an impact on tree shaking

import * as _ from 'lodash'; console.log( _.uniqueId() ); // This will include the whole lodash
import { uniqueId } from 'lodash'; console.log( uniqueId() ); // This will include only uniqueId

@@ -0,0 +1,73 @@
import mocha from 'mocha';
Copy link
Contributor Author

@youknowriad youknowriad Mar 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use the /** External/Internal dependencies ** comments (similar to Calypso)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, probably :)

@nylen
Copy link
Member

nylen commented Mar 17, 2017

In e157c9a I changed name to slug. So far, we're really dealing with the internal ID of the block, which I am calling a slug because that's the WordPress term for a dashed identifier, including a plugin's ID.

I'm open to suggestions here, but I don't think it should be called name. The name is the human-readable block name, which doesn't exist yet but will be different and probably not necessarily unique.

@youknowriad
Copy link
Contributor Author

I ignored yarn.lock, but maybe we should consider committing this file instead?

On the initial build PR. I've used yarn as well but if we commit the yarn.lock, all contributors should use yarn to avoid making yarn.lock out of sync when adding dependencies. @aduth raised a good point, that this would add a new thing to learn for contributors, and we may want to stick with npm for now

@youknowriad
Copy link
Contributor Author

youknowriad commented Mar 17, 2017

This is looking solid for me. Thanks, @nylen for the test infra. Waiting for a 👍 to merge. We could iterate later if we need to change the block slug format or anything else.

Copy link
Member

@aduth aduth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you look to see if there's a chance we could integrate QUnit without too much trouble? Seems like it will suffer many of the same gotchas as Mocha, but if we could pull it off would certainly make eventual integration a fair bit less... complicated.


import * as blocks from '@wordpress/blocks';

chai.use( dirtyChai );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're going to pull in and assume plugins to be used consistently, could we somehow apply it as part of the overall test initialization?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I extracted this to a test/boot.js

chai.use( dirtyChai );

describe( 'blocks API', () => {
// TODO: We probably want a way to undo this, and split this logic out into
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would blocks.unregisterBlock be a reasonable reasonable to work around this problem?


const config = module.exports = Object.assign( {}, baseConfig, {
target: 'node',
externals: [ nodeExternals() ],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see it in mocha-webpack's docs, and even upon reading the docs of webpack-node-externals itself, it's still not clear why this is necessary. Does it make the build more performant? Is it any different than:

resolve: {
	modules: baseConfig.resolve.modules.filter( ( source ) => source !== 'node_modules' )
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried without it and using your alternative. I don't understand all what's happening here but it's not working without this.

package.json Outdated
@@ -11,23 +11,30 @@
"editor"
],
"scripts": {
"test": "mocha-webpack test/",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seeing that mocha-webpack has a --webpack-config option and our Webpack config already includes some environment-specific handling, do you have any opinion on whether it might make sense to merge webpack.config.test.js into the base config and change this to:

"test": "NODE_ENV=test mocha-webpack --webpack-config webpack.config.js test/"

Trying to think of ways to avoid root directory becoming too sprawling with files.

@youknowriad youknowriad force-pushed the update/implement-block-registering branch from e157c9a to 5904546 Compare March 20, 2017 09:46
@aduth
Copy link
Member

aduth commented Mar 21, 2017

Issue with mocha-webpack is that it won't respect our custom entry definitions:

Maybe you noticed that entry, output.filename and output.path are completely missing in this config. mocha-webpack does this automatically for you and if you would specify it anyway, it will be overridden by mocha-webpack.

https://www.npmjs.com/package/mocha-webpack

- No need for a separate `validateBlockSlug` function
- More thorough and more isolated tests
@nylen
Copy link
Member

nylen commented Mar 21, 2017

Thanks, @youknowriad and @aduth for working on this and for reviewing.

In a512271 I added blocks.unregisterBlock, which allowed for some improvements to the tests and for getting rid of the separate function for validateBlockSlug.

I'm not having any problems with npm test or npm run build, and I don't follow the globals issues mentioned in #289 (comment). Is there anything left to do here?

One remaining question here is whether to use QUnit or Mocha for test infrastructure. I find QUnit very awkward to work with.

In terms of eventual integration with WP core, in the worst case this is going to mean that we add a separate Travis CI job to run Mocha tests. I think this is OK, because there will be lots of tests for this project, so they will take a while to run 😉

I think the added developer convenience of using Mocha is well worth it, and maybe we can also start a new standard for core JS tests.

So, in short, IMO this is ready to merge so we can move forward.

This was from `mocha-webpack`, which is no longer used, so this entry is
no longer needed.
@youknowriad youknowriad dismissed aduth’s stale review March 22, 2017 08:48

Comments addressed

@youknowriad
Copy link
Contributor Author

youknowriad commented Mar 22, 2017

I don't think it's related to this PR, but we're having errors like TypeError: n(...) is not a function on the plugin page. Seems like a conflict between the webpack build and some WordPress global variable? Any idea about this?

@nylen
Copy link
Member

nylen commented Mar 22, 2017

I think those errors are related to this PR. Here are the ones I'm seeing (and none in master after a rebuild):

2017-03-22t09 30 23-0300

Seems like we need to add some integration tests for the plugin page as well.

@youknowriad
Copy link
Contributor Author

I found that babel transform-runtime plugin is causing these errors, but I can't find why exactly.

@youknowriad
Copy link
Contributor Author

Replacing transform-runtime with babel-polyfill fixes the bug, but we can not include the polyfill in the three modules (duplicate), I'm thinking of adding the polyfill as an external script tag. Thoughts?

@aduth
Copy link
Member

aduth commented Mar 22, 2017

65066e1 fixes errors from Webpack.

There's another error from registerBlock for how we're using old signature for the text block. Throwing errors crashes the entire editor initialization. Maybe a little heavy handed? Perhaps we can consider console.error instead.

@youknowriad
Copy link
Contributor Author

Thanks for the fix @aduth I fixed the npm test command to correctly check lint errors and fix the remaining lint issues in b95c775

@youknowriad
Copy link
Contributor Author

I fixed the last registerBlock error in e5f72ca and the React minification warning. I'm rebasing (too many junk commits) and merging. Tks @aduth and @nylen. We can make changes to these on follow-up PRs if needed

@youknowriad youknowriad merged commit ffd1cef into master Mar 22, 2017
@youknowriad youknowriad deleted the update/implement-block-registering branch March 22, 2017 14:09
@aduth
Copy link
Member

aduth commented Mar 22, 2017

I'd still like some feedback on my earlier point:

Throwing errors crashes the entire editor initialization. Maybe a little heavy handed? Perhaps we can consider console.error instead.

@youknowriad
Copy link
Contributor Author

@aduth I agree, We shouldn't crash on wrong blocks, but just ignore them.

👍 for console.error for me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Framework Issues related to broader framework topics, especially as it relates to javascript
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants