-
Notifications
You must be signed in to change notification settings - Fork 52
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
JavaScript String Extraction #26
Conversation
While it's not implied, just to note that a common point of confusion is that its implementation as a Babel plugin has something to do with an opt-in decision to use newer language syntax, when in fact it was largely a combination of (a) taking advantage of the Babylon parser which, being at the core of Babel's language transpilation, is assumed to be well-maintained and (b) to combine into the same transpilation pipeline if one decides to use newer language features (as Gutenberg does, avoiding two separate parse steps). |
While it may be implemented as such in its current form, there's nothing which requires this to be a two-step process. |
@aduth Totally get the point. There's nothing wrong with that and I don't think we can get around that anyway. Although JSX support is planned for Peast (the parser used in this PR), it makes more sense to use something like the Babel plugin for core and Gutenberg. That's why I've been working on things like #31 to make it easier to use both hand in hand. As mentioned in yesterday's meeting, we'll likely test such a setup with Gutenberg and some other plugin(s) on WordPress.org soon. That way one at least won't have to generate these PHP files anymore during a build. |
Useful when this is done in another build step, e.g. through Babel.
Did some updates today:
|
src/MakePotCommand.php
Outdated
@@ -140,7 +153,7 @@ protected function retrieve_main_file_data() { | |||
|
|||
$files = new IteratorIterator( new DirectoryIterator( $this->source ) ); | |||
|
|||
/** @var DirectoryIterator $file */ | |||
/* @var DirectoryIterator $file */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a real docblock /** */
, but inside of the foreach
block as the first line, as $file
only exists within the foreach
block.
This will then allow an IDE to know how to handle $file
in there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@schlessera What IDE do you use? PhpStorm for example handles this just fine outside the foreach
.
In WordPress core both /**
and /*
is used for these annotations. Not sure if WPCS would think the former is a badly formatted doc block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I use PHPStorm as well, and for a few versions now, it interprets this correctly. But technically, the variable does not exist within the scope where you qualify it, and some other editors and static analysis tools will flag this.
I usually do one of these two instead:
foreach ( $files as $file ) {
/** @var DirectoryIterator $file */
if ( $file->isFile() && $file->isReadable() && 'php' === $file->getExtension()) {
$plugin_files[] = $file->getRealPath();
}
}
foreach ( $files as /** @var DirectoryIterator $file */ $file ) {
if ( $file->isFile() && $file->isReadable() && 'php' === $file->getExtension()) {
$plugin_files[] = $file->getRealPath();
}
}
Regarding the docblock:
Although PHPStorm is generally very lax in this regard, using @var
inside of a regular multiline comment does not have any semantic meaning whatsoever. Tags only have a proper meaning within docblocks. Again, some tools are more strict about this and will ignore the @var
if it is not within a proper docblock.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, nevermind my comments about moving the docblock inside of the foreach
. This is apparently handled as an exception in the proposed PSR: https://github.com/php-fig/fig-standards/blob/master/proposed/phpdoc.md
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, the above standard proposal also states that tags need to be within a docblock, not a regular comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good to know, thanks.
I moved it into the foreach
now and use a proper docblock and everything still works :-)
src/MakePotCommand.php
Outdated
$this->slug = Utils\get_flag_value( $assoc_args, 'slug', Utils\basename( $this->source ) ); | ||
$this->source = realpath( $args[0] ); | ||
$this->slug = Utils\get_flag_value( $assoc_args, 'slug', Utils\basename( $this->source ) ); | ||
$this->scan_js = ! Utils\get_flag_value( $assoc_args, 'skip-js', ! $this->scan_js ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer the property to be called $skip_js
, to avoid the weird double negation here. As it is only used once, just directly mapping the parameter seems clearer to me.
src/JsFunctionsScanner.php
Outdated
use Peast\Syntax\Node\Node; | ||
use Peast\Syntax\Node\VariableDeclaration; | ||
use Peast\Traverser; | ||
use Peast\Syntax\Node\CallExpression; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of these Node\Something
imports are a case where I would prefer the use of relative namespaces.
So, have only 1 import Peast\Syntax\Node
here, and then use relative namespaces like Node\Identifier
or Node\Literal
in the code.
This makes the import list less daunting, and it gives more context to the consuming code, where it is clear that these are all different types of nodes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I now see that the library has a Node\Node
, which is unfortunate and might make my above suggestion weird.
I usually build my code so that the interface is outside of the namespace with the implementations. This way, you can use Node
as well as Node\Literal
, ...
I'll let you decide what you find cleaner, but having a Node\Node
inside the code is certainly not really elegant.
src/JsFunctionsScanner.php
Outdated
use Peast\Syntax\Node\Identifier; | ||
use Peast\Syntax\Node\Literal; | ||
|
||
class JsFunctionsScanner extends GettextJsFunctionsScanner { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Class should be final
and protected methods/properties should be private (unless they were already protected in the superclass).
src/PhpCodeExtractor.php
Outdated
@@ -11,7 +11,7 @@ | |||
use SplFileInfo; | |||
use WP_CLI; | |||
|
|||
class WordPressCodeExtractor extends PhpCode { | |||
class PhpCodeExtractor extends PhpCode { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Class should be final
and protected methods/properties should be private (unless they were already protected in the superclass).
src/JsFunctionsScanner.php
Outdated
$functions = $options['functions']; | ||
$file = $options['file']; | ||
|
||
/* @var Node $node */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a real docblock inside of the foreach
block.
src/JsCodeExtractor.php
Outdated
RecursiveIteratorIterator::CHILD_FIRST | ||
); | ||
|
||
/* @var \DirectoryIterator $file */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a real docblock inside of the foreach
block.
src/PhpCodeExtractor.php
Outdated
RecursiveIteratorIterator::CHILD_FIRST | ||
); | ||
|
||
/* @var \DirectoryIterator $file */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a real docblock inside of the foreach
block.
src/PhpCodeExtractor.php
Outdated
RecursiveIteratorIterator::CHILD_FIRST | ||
); | ||
|
||
/* @var \DirectoryIterator $file */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be imported instead of using a FQCN.
src/JsCodeExtractor.php
Outdated
RecursiveIteratorIterator::CHILD_FIRST | ||
); | ||
|
||
/* @var \DirectoryIterator $file */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be imported instead of using a FQCN.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One could also argue that this would result in an unused import statement as it's only used for the code comment.
Is this defined in the coding standards somewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The coding standards don't deal with namespaces, so there's no imports either.
It is not an unused import, as you're referencing it through the docblock. Note that PHP annotations like @var
are considered parsable and executable code. So the annotation is something like new var( DirectorIterator::class, 'file' )
.
🎉 We will probably still need to iterate on this in the future, but having it merged now means we can better test it in actual use cases. |
JavaScript String Extraction
More and more plugins rely heavily on JavaScript and the JS I18N library established by Gutenberg.
Since neither makepot.php nor this WP-CLI command support JavaScript, they had to build their own Babel plugin to extract JavaScript files.
However, one cannot expect users to a) use Babel b) use two tools to generate a simple POT file.
So, why not support this in this little WP-CLI command here? :-)
Even though the Gettext library we use for the PHP part has a JS functions scanner, it is very limited: no support for comments, no clear indication which ECMAScript standard is supported, no usage of an AST, which is really important with such a quickly evolving language.
I decided to use Peast to traverse a script's AST, which was incredibly easy. We might even consider suggesting this for the Gettext library.
Still needs some tests, but so far it works fine.
Fixes #30.