Skip to content

Commit 918c94e

Browse files
authored
Comment commands (#6)
* Add node event handler * Implement simple/node/level language levels as comments/keyword * Document-language keyword support prototype * Add CodeClimate plugins * Track node origin * Apply fixes from StyleCI (#5) * Drop @document-language * Update CodeClimate config * Reduce conditions nesting * Cleanup and add test * Fix code style * Set Phan options * Isolate compilation stuff into a trait
1 parent 0fb2334 commit 918c94e

File tree

10 files changed

+473
-91
lines changed

10 files changed

+473
-91
lines changed

.codeclimate.yml

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
2-
engines:
2+
version: "2"
3+
plugins:
34
duplication:
45
enabled: true
56
exclude_paths:
@@ -11,6 +12,27 @@ engines:
1112
enabled: true
1213
phpmd:
1314
enabled: true
15+
phpcodesniffer:
16+
enabled: true
17+
SonarPHP:
18+
enabled: true
19+
phan:
20+
enabled: true
21+
checks:
22+
PhanUndeclaredExtendedClass:
23+
enabled: false
24+
PhanUndeclaredTypeParameter:
25+
enabled: false
26+
PhanUndeclaredClass:
27+
enabled: false
28+
PhanUndeclaredClassMethod:
29+
enabled: false
30+
PhanUndeclaredClassConstant:
31+
enabled: false
32+
PhanUndeclaredClassInstanceof:
33+
enabled: false
34+
PhanUndeclaredTypeReturnType:
35+
enabled: false
1436
ratings:
1537
paths:
1638
- "**.php"

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"php": ">=7.0",
1414
"js-phpize/js-phpize": "^2.0.0",
1515
"phug/compiler": "^0.5.0",
16-
"phug/formatter": "^0.5.42"
16+
"phug/formatter": "^0.5.43"
1717
},
1818
"require-dev": {
1919
"phpunit/phpunit": ">=4.8.35 <6.0",

src/JsPhpize/JsPhpizePhug.php

Lines changed: 107 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,24 @@
33
namespace JsPhpize;
44

55
use Exception;
6-
use JsPhpize\Compiler\Exception as CompilerException;
7-
use JsPhpize\Lexer\Exception as LexerException;
8-
use JsPhpize\Parser\Exception as ParserException;
6+
use JsPhpize\Traits\Compilation;
97
use Phug\AbstractCompilerModule;
108
use Phug\Compiler;
9+
use Phug\Compiler\Event\NodeEvent;
1110
use Phug\CompilerEvent;
12-
use Phug\CompilerInterface;
11+
use Phug\Formatter\Element\KeywordElement;
12+
use Phug\Parser\Node\CommentNode;
13+
use Phug\Parser\Node\KeywordNode;
14+
use Phug\Parser\Node\TextNode;
1315
use Phug\Renderer;
1416
use Phug\Util\ModuleContainerInterface;
1517

1618
class JsPhpizePhug extends AbstractCompilerModule
1719
{
20+
use Compilation;
21+
22+
protected $languages = ['js', 'php'];
23+
1824
public function __construct(ModuleContainerInterface $container)
1925
{
2026
parent::__construct($container);
@@ -35,6 +41,7 @@ public function __construct(ModuleContainerInterface $container)
3541

3642
//Set default options
3743
$this->setOptionsRecursive([
44+
'language' => 'js',
3845
'allowTruncatedParentheses' => true,
3946
'catchDependencies' => true,
4047
'ignoreDollarVariable' => true,
@@ -46,111 +53,122 @@ public function __construct(ModuleContainerInterface $container)
4653
//Apply options from container
4754
$this->setOptionsRecursive($compiler->getOption(['module_options', 'jsphpize']));
4855

56+
$compiler->attach(CompilerEvent::NODE, [$this, 'handleNodeEvent']);
57+
4958
$compiler->setOptionsRecursive([
59+
'keywords' => [
60+
'language' => [$this, 'handleLanguageKeyword'],
61+
'node-language' => [$this, 'handleNodeLanguageKeyword'],
62+
],
5063
'patterns' => [
51-
'transform_expression' => function ($jsCode) use ($compiler) {
52-
$jsPhpize = $this->getJsPhpizeEngine($compiler);
53-
$compilation = $this->compile($jsPhpize, $jsCode, $compiler->getPath());
54-
55-
if (!($compilation instanceof Exception)) {
56-
return $compilation;
57-
}
58-
59-
return $jsCode;
64+
'transform_expression' => function ($code) use ($compiler) {
65+
return $this->transformExpression($this->getJsPhpizeEngine($compiler), $code, $compiler->getPath());
6066
},
6167
],
6268
'checked_variable_exceptions' => [
63-
'js-phpize' => function ($variable, $index, $tokens) {
64-
return $index > 2 &&
65-
$tokens[$index - 1] === '(' &&
66-
$tokens[$index - 2] === ']' &&
67-
is_array($tokens[$index - 3]) &&
68-
$tokens[$index - 3][0] === T_CONSTANT_ENCAPSED_STRING &&
69-
preg_match('/_with_ref\'$/', $tokens[$index - 3][1]);
70-
},
69+
'js-phpize' => [static::class, 'checkedVariableExceptions'],
7170
],
7271
]);
7372
}
7473

75-
/**
76-
* @return JsPhpize
77-
*/
78-
public function getJsPhpizeEngine(CompilerInterface $compiler)
74+
public function handleNodeEvent(NodeEvent $event)
7975
{
80-
if (!$compiler->hasOption('jsphpize_engine')) {
81-
$compiler->setOption('jsphpize_engine', new JsPhpize($this->getOptions()));
76+
/* @var CommentNode $node */
77+
if (($node = $event->getNode()) instanceof CommentNode &&
78+
!$node->isVisible() &&
79+
$node->hasChildAt(0) &&
80+
($firstChild = $node->getChildAt(0)) instanceof TextNode &&
81+
preg_match(
82+
'/^@((?:node-)?lang(?:uage)?)([\s(].*)$/',
83+
trim($firstChild->getValue()),
84+
$match
85+
)
86+
) {
87+
$keyword = new KeywordNode(
88+
$node->getToken(),
89+
$node->getSourceLocation(),
90+
$node->getLevel(),
91+
$node->getParent()
92+
);
93+
$keyword->setName($match[1]);
94+
$keyword->setValue($match[2]);
95+
$event->setNode($keyword);
8296
}
97+
}
98+
99+
protected function getLanguageKeywordValue($value, KeywordElement $keyword, $name)
100+
{
101+
$value = trim($value, "()\"' \t\n\r\0\x0B");
102+
103+
if (!in_array($value, $this->languages)) {
104+
$file = 'unknown';
105+
$line = 'unknown';
106+
$offset = 'unknown';
107+
$node = $keyword->getOriginNode();
108+
if ($node && ($location = $node->getSourceLocation())) {
109+
$file = $location->getPath();
110+
$line = $location->getLine();
111+
$offset = $location->getOffset();
112+
}
83113

84-
return $compiler->getOption('jsphpize_engine');
114+
throw new \InvalidArgumentException(sprintf(
115+
"Invalid argument for %s keyword: %s. Possible values are: %s\nFile: %s\nLine: %s\nOffset:%s",
116+
$name,
117+
$value,
118+
implode(', ', $this->languages),
119+
$file,
120+
$line,
121+
$offset
122+
));
123+
}
124+
125+
return $value;
85126
}
86127

87-
/**
88-
* @param JsPhpize $jsPhpize
89-
* @param int $code
90-
* @param string $fileName
91-
*
92-
* @throws Exception
93-
*
94-
* @return Exception|string
95-
*/
96-
public function compile(JsPhpize $jsPhpize, $code, $fileName)
128+
public function handleNodeLanguageKeyword($value, KeywordElement $keyword, $name)
97129
{
98-
try {
99-
$phpCode = trim($jsPhpize->compile($code, $fileName ?: 'raw string'));
100-
$phpCode = preg_replace('/\{\s*\}$/', '', $phpCode);
101-
$phpCode = preg_replace(
102-
'/^(?<!\$)\$+(\$[a-zA-Z\\\\\\x7f-\\xff][a-zA-Z0-9\\\\_\\x7f-\\xff]*\s*[=;])/',
103-
'$1',
104-
$phpCode
105-
);
130+
$value = $this->getLanguageKeywordValue($value, $keyword, $name);
106131

107-
return rtrim(trim($phpCode), ';');
108-
} catch (Exception $exception) {
109-
if (
110-
$exception instanceof LexerException ||
111-
$exception instanceof ParserException ||
112-
$exception instanceof CompilerException
113-
) {
114-
return $exception;
115-
}
132+
if ($next = $keyword->getNextSibling()) {
133+
$next->prependChild(new KeywordElement('language', $value, $keyword->getOriginNode()));
134+
$next->appendChild(new KeywordElement('language', $this->getOption('language'), $keyword->getOriginNode()));
135+
}
136+
137+
return '';
138+
}
139+
140+
public function handleLanguageKeyword($value, KeywordElement $keyword, $name)
141+
{
142+
$value = $this->getLanguageKeywordValue($value, $keyword, $name);
143+
144+
$this->setOption('language', $value);
116145

117-
throw $exception;
146+
return '';
147+
}
148+
149+
protected function transformExpression(JsPhpize $jsPhpize, $code, $fileName)
150+
{
151+
if ($this->getOption('language') === 'php') {
152+
return $code;
118153
}
154+
155+
$compilation = $this->compile($jsPhpize, $code, $fileName);
156+
157+
if (!($compilation instanceof Exception)) {
158+
return $compilation;
159+
}
160+
161+
return $code;
119162
}
120163

121-
/**
122-
* @return array
123-
*/
124-
public function getEventListeners()
164+
public static function checkedVariableExceptions($variable, $index, $tokens)
125165
{
126-
return [
127-
CompilerEvent::OUTPUT => function (Compiler\Event\OutputEvent $event) {
128-
/** @var CompilerInterface $compiler */
129-
$compiler = $event->getTarget();
130-
$jsPhpize = $this->getJsPhpizeEngine($compiler);
131-
$output = preg_replace(
132-
'/\{\s*\?><\?(?:php)?\s*\}/',
133-
'{}',
134-
$event->getOutput()
135-
);
136-
$output = preg_replace(
137-
'/\}\s*\?><\?(?:php)?\s*(' .
138-
'else(if)?|for|while|switch|function' .
139-
')(?![a-zA-Z0-9_])/',
140-
'} $1',
141-
$output
142-
);
143-
144-
$dependencies = $jsPhpize->compileDependencies();
145-
if ($dependencies !== '') {
146-
$output = $compiler->getFormatter()->handleCode($dependencies) . $output;
147-
}
148-
149-
$event->setOutput($output);
150-
151-
$jsPhpize->flushDependencies();
152-
$compiler->unsetOption('jsphpize_engine');
153-
},
154-
];
166+
return $index > 2 &&
167+
$tokens[$index - 1] === '(' &&
168+
$tokens[$index - 2] === ']' &&
169+
!preg_match('/^__?pug_/', $variable) &&
170+
is_array($tokens[$index - 3]) &&
171+
$tokens[$index - 3][0] === T_CONSTANT_ENCAPSED_STRING &&
172+
preg_match('/_with_ref\'$/', $tokens[$index - 3][1]);
155173
}
156174
}

0 commit comments

Comments
 (0)