├── .gitignore ├── LICENSE ├── README.md ├── Vue_Base.php ├── build.js ├── components ├── vue-com-test │ └── index.vue ├── vue-com │ └── index.vue ├── vue-dir │ └── index.vue ├── vue-event │ └── index.vue ├── vue-filter │ └── index.vue ├── vue-for │ └── index.vue ├── vue-form │ └── index.vue ├── vue-if │ └── index.vue ├── vue-klass │ └── index.vue ├── vue-shorthand │ └── index.vue ├── vue-style │ └── index.vue ├── vue-transition │ └── index.vue └── vue-val │ └── index.vue ├── imgs └── reason.png ├── index.js ├── index.php ├── package.json ├── test.js └── vue-template-php-compiler ├── README.md ├── build.js ├── index.js ├── package.json ├── vuescript.js └── vuescript.pegjs /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .vscode 4 | components/*/*.js 5 | components/*/*.php -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 JoeRay61 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-php 2 | 3 | 使用php进行vue的服务端渲染方案 4 | 5 | ## 本地运行 6 | 7 | ``` shell 8 | $ git clone git@github.com:Joe3Ray/vue-php.git 9 | $ cd vue-php 10 | $ npm install 11 | $ npm run build 12 | $ npm test 13 | ``` 14 | 15 | ## 项目文件说明 16 | 17 | - `index.js`: 将`.vue`编译成`.js`和`.php`的脚本,编译出来的`.js`用于前端渲染,`.php`用于服务端的渲染 18 | - `build.js`: 批量将`components`中的各个组件调用`index.js`来编译的脚本 19 | - `Vue_Base.php`: 使用php实现的Vue基类,包含渲染功能 20 | - `index.php`: 调用php版的Vue组件class渲染出html字符串的示例 21 | - `test.js`: 单元测试脚本 22 | - `componenets/**`: 组件示例 23 | - `vue-template-php-compiler`: 根据`vue-template-compiler`修改而来,能够编译出php版本`render`函数的工具 24 | 25 | ## Vue服务端渲染原理 26 | 27 | ![img](imgs/reason.png) 28 | 29 | ## 使用限制 30 | 31 | 由于不同语言的限制,我们在使用`vue-php`进行服务端渲染时也有一些限制: 32 | 33 | - `.vue`文件中,组件的`data`和`components`属性必须单独放到``中,并且是`json`形式 34 | - `template`中模板使用js表达式时,不能调用函数,不能使用计算属性 35 | 36 | ## 接入 37 | 38 | 如果想要接入这一套`vue-php`的方案,需要在线下使用`index.js`对`.vue`文件进行编译,将编译出来的`.php`上线到服务器,并且服务器端引入`VNode.php`和`Vue_Base.php` 39 | 40 | ## License 41 | 42 | [MIT](https://opensource.org/licenses/MIT) -------------------------------------------------------------------------------- /Vue_Base.php: -------------------------------------------------------------------------------- 1 | tag = $tag; 35 | $this->data = $data; 36 | $this->children = $children; 37 | $this->text = $text; 38 | $this->elm = $elm; 39 | $this->context = $context; 40 | if (is_array($data) && isset($data['key'])) { 41 | $this->key = $data['key']; 42 | } 43 | $this->componentOptions = $componentOptions; 44 | } 45 | } 46 | 47 | /** 48 | * class Vue_Base 49 | * Vue基类 50 | * 所有Vue组件都继承自Vue_Base 51 | */ 52 | class Vue_Base { 53 | 54 | public function getTagNamespace($tag) { 55 | return false; 56 | } 57 | 58 | public function isReservedTag($tag) { 59 | return false; 60 | } 61 | 62 | public function parsePlatformTagName($tag) { 63 | return $tag; 64 | } 65 | 66 | // 是否数字或字符串 67 | public function isPrimitive($val) { 68 | if (is_numeric($val) || is_string($val)) { 69 | return true; 70 | } 71 | return false; 72 | } 73 | 74 | // 组件目录 75 | private $componentsDir = array('./'); 76 | 77 | // 设置组件目录 78 | public function setComponentsDir($dirs) { 79 | if (isset($dirs)) { 80 | if (is_string($dirs)) { 81 | array_unshift($this->componentsDir, $dirs); 82 | } 83 | elseif ($this->isPlainArray($dirs)) { 84 | foreach($dirs as $dir) { 85 | if (is_string($dir)) { 86 | array_unshift($this->componentsDir, $dir); 87 | } 88 | } 89 | } 90 | } 91 | } 92 | 93 | // VNode工厂 94 | public function generateVNode($tag=null, $data=array(), $children=null, $text=null, $elm=null, $context=null, $componentOptions=null) { 95 | $node = new VNode($tag, $data, $children, $text, $elm, $context, $componentOptions); 96 | return $node; 97 | } 98 | 99 | // 是否索引数组 100 | public function isPlainArray($arr) { 101 | if (is_array($arr)) { 102 | $keys = array_keys($arr); 103 | return $keys === array_keys($keys); 104 | } 105 | return false; 106 | } 107 | 108 | // 创建一个空的虚拟dom 109 | public function createEmptyVNode() { 110 | $node = $this->generateVNode(); 111 | $node->text = ''; 112 | $node->isComment = true; 113 | return $node; 114 | } 115 | 116 | // 创建文本虚拟dom 117 | public function createTextVNode($text) { 118 | return $this->generateVNode(null, null, null, (string)$text); 119 | } 120 | 121 | public function normalizeArrayChildren($children, $nestedIndex='') { 122 | $res = array(); 123 | for ($i = 0; $i < count($children); $i++) { 124 | $c = $children[$i]; 125 | if ($c == null || is_bool($c)) { 126 | continue; 127 | } 128 | if (count($res) > 1) { 129 | $last = $res[count($res) - 1]; 130 | } 131 | // nested 132 | if ($this->isPlainArray($c)) { 133 | $res = array_merge($res, $this->normalizeArrayChildren($c, $nestedIndex . '_' . $i)); 134 | } 135 | elseif ($this->isPrimitive($c)) { 136 | if (isset($last) && isset($last->text)) { 137 | $last->text .= (string)$c; 138 | } 139 | elseif ($c !== '') { 140 | array_push($res, $this->createTextVNode($c)); 141 | } 142 | } 143 | else { 144 | if ($c->text && isset($last) && $last->text) { 145 | $res[count($res) - 1] = $this->createTextVNode($last->text . $c->text); 146 | } 147 | else { 148 | if ($c->tag && $c->key == null && $nestedIndex != null) { 149 | $c->key = '__vlist' . $nestedIndex . '_' . $i . '__'; 150 | } 151 | array_push($res, $c); 152 | } 153 | } 154 | } 155 | return $res; 156 | } 157 | 158 | // 处理$children 159 | public function normalizeChildren($children) { 160 | return $this->isPrimitive($children) ? array($this->createTextVNode($children)) : ($this->isPlainArray($children) ? $this->normalizeArrayChildren($children) : null); 161 | } 162 | 163 | public function simpleNormalizeChildren($children) { 164 | for ($i = 0; $i < count($children); $i++) { 165 | if ($this->isPlainArray($children)) { 166 | return array_merge(array(), $children); 167 | } 168 | } 169 | return $children; 170 | } 171 | 172 | // 驼峰化 173 | public function camelize($str) { 174 | preg_match_all('/-(\w)/', $str, $match); 175 | foreach ($match[0] as $i => $e) { 176 | $str = str_replace($e, strtoupper($match[1][$i]), $str); 177 | } 178 | return $str; 179 | } 180 | 181 | // Pascal化 182 | public function capitalize($str) { 183 | $arr = str_split($str); 184 | $arr[0] = strtoupper($arr[0]); 185 | return join('', $arr); 186 | } 187 | 188 | // 获取Vue实例相关信息 189 | public function resolveAsset($id) { 190 | if (!is_string($id)) { 191 | return; 192 | } 193 | if (isset($this->options['components']) && isset($this->options['components'][$id])) { 194 | foreach ($this->componentsDir as $dir) { 195 | $path = $dir . '/' . $id . '/index.php'; 196 | $path = str_replace('//', '/', $path); 197 | if (file_exists($path)) { 198 | return $path; 199 | } 200 | } 201 | } 202 | } 203 | 204 | public function applyNS($vnode, $ns) { 205 | $vnode['ns'] = $ns; 206 | if ($vnode['ns'] === 'foreignObject') { 207 | return; 208 | } 209 | if (is_array($vnode['children'])) { 210 | for ($i = 0; $i < count($vnode['children']); $i++) { 211 | $child = $vnode['children'][$i]; 212 | if ($child['tag'] && !$child['ns']) { 213 | $this->applyNS($child, $ns); 214 | } 215 | } 216 | } 217 | } 218 | 219 | // 数据混合 220 | public function extendData($to, $from) { 221 | foreach ($from as $k => $v) { 222 | $to[$k] = $v; 223 | } 224 | return $to; 225 | } 226 | 227 | // 创建元素 228 | public function createElement($context, $tag, $data, $children, $normalizationType=1, $alwaysNormalize=null) { 229 | if ($this->isPlainArray($data) || $this->isPrimitive($data)) { 230 | $normalizationType = $children; 231 | $children = $data; 232 | $data = null; 233 | } 234 | if ($alwaysNormalize) { 235 | $normalizationType = 2; 236 | } 237 | 238 | if (!$tag) { 239 | return $this->createEmptyVNode(); 240 | } 241 | 242 | if ($normalizationType === 2) { 243 | $children = $this->normalizeChildren($children); 244 | } 245 | elseif ($normalizationType === 1) { 246 | $children = $this->simpleNormalizeChildren($children); 247 | } 248 | $vnode = null; 249 | $ns = null; 250 | if (is_string($tag)) { 251 | $Ctor = null; 252 | $ns = $this->getTagNamespace($tag); 253 | if ($this->isReservedTag($tag)) { 254 | $vnode = $this->generateVNode($this->parsePlatformTagName($tag), $data, $children, null, null, $context); 255 | } 256 | // 针对 keep-alive / transition 这2个内置组件 257 | // 直接取子节点 258 | elseif ($tag === 'keep-alive' || $tag === 'transition') { 259 | $vnode = $this->generateVNode(null, null, $children); 260 | $vnode->skipToChildren = true; 261 | } 262 | // 针对transition-group 263 | elseif ($tag === 'transition-group') { 264 | $tag = 'span'; 265 | if (is_array($data) && is_array($data['attrs'])) { 266 | if (isset($data['attrs']['tag'])) { 267 | $tag = $data['attrs']['tag']; 268 | unset($data['attrs']['tag']); 269 | } 270 | $innerAttrs = array( 271 | 'name','appear','css','mode','type','enter-class','leave-class', 272 | 'enter-to-class','leave-to-class','enter-active-class','leave-active-class', 273 | 'appear-class','appear-active-class','appear-to-class' 274 | ); 275 | foreach ($data['attrs'] as $k => $v) { 276 | if (in_array($k, $innerAttrs)) { 277 | unset($data['attrs'][$k]); 278 | } 279 | } 280 | } 281 | $vnode = $this->generateVNode($tag, $data, $children, null, null, $context); 282 | 283 | } 284 | // 根据attrs.tag(默认span)设置当前节点 285 | elseif ($Ctor = $this->resolveAsset($tag)) { 286 | $vnode = $this->createComponent($Ctor, $data, $context, $children, $tag); 287 | } 288 | else { 289 | $vnode = $this->generateVNode($tag, $data, $children, null, null, $context); 290 | } 291 | } 292 | /* 293 | else { 294 | $vnode = $this->createComponent($tag, $data, $context, $children); 295 | } 296 | */ 297 | if ($vnode) { 298 | if ($ns) { 299 | $this->applyNS($vnode, $ns); 300 | } 301 | return $vnode; 302 | } 303 | else { 304 | return $this->createEmptyVNode(); 305 | } 306 | } 307 | 308 | public function handleClsName($tag) { 309 | $str = str_replace('-', '_', $tag); 310 | $arr = explode('_', $str); 311 | $res = array(); 312 | foreach ($arr as $v) { 313 | $v[0] = strtoupper($v[0]); 314 | $res[] = $v; 315 | } 316 | $str = join('_', $res); 317 | return $str; 318 | } 319 | 320 | // 创建组件 321 | public function createComponent($Ctor, $data, $context, $children, $tag) { 322 | if (!$Ctor) { 323 | return; 324 | } 325 | 326 | $vnode = $this->generateVNode($tag, $data); 327 | $vnode->isComponent = true; 328 | 329 | include_once($Ctor); 330 | $cls = $this->handleClsName($tag); 331 | $component = new $cls(); 332 | // 处理slot 333 | if ($children && count($children) > 0) { 334 | foreach ($children as $child) { 335 | $slotName = 'default'; 336 | if ($child->data && $child->data['slot']) { 337 | $slotName = $child->data['slot']; 338 | } 339 | if (!isset($component->slots[$slotName]) || !is_array($component->slots[$slotName])) { 340 | $component->slots[$slotName] = array(); 341 | } 342 | array_push($component->slots[$slotName], $child); 343 | } 344 | } 345 | 346 | // 处理scopedSlots 347 | if (isset($data['scopedSlots']) && is_array($data['scopedSlots'])) { 348 | $component->scopedSlots = $data['scopedSlots']; 349 | } 350 | 351 | // 处理props 352 | $props = array(); 353 | if (isset($data['attrs'])) { 354 | $props = $data['attrs']; 355 | } 356 | foreach ($props as $k => $v) { 357 | $component->_d[$k] = $v; 358 | } 359 | $vnode->children = array($component->_render($component)); 360 | return $vnode; 361 | } 362 | public function _f_lower($args) { 363 | $val = $args[0]; 364 | return strtolower($val); 365 | } 366 | 367 | public function defaultFilter($args) { 368 | $val = $args[0]; 369 | if ($this->isPrimitive($val)) { 370 | return $val; 371 | } 372 | else { 373 | return ''; 374 | } 375 | } 376 | public $_d = array(); 377 | 378 | public $options = array(); 379 | 380 | public $scopedSlots = array(); 381 | 382 | public $slots = array(); 383 | 384 | public function __construct() { 385 | 386 | } 387 | 388 | // call static render function 389 | public function _m($index) { 390 | $methodName = "_m" . $index; 391 | if (method_exists($this, $methodName)) { 392 | return $this->$methodName($this); 393 | } 394 | return null; 395 | } 396 | 397 | // create element 398 | public function _c($a=null, $b=null, $c=null, $d=null) { 399 | if (isset($b['scopedSlots'])) { 400 | $this->scopedSlots = $b['scopedSlots']; 401 | } 402 | 403 | return $this->createElement($this, $a, $b, $c, $d, false); 404 | } 405 | 406 | // create text node 407 | public function _v($str) { 408 | return $this->createTextVNode($str); 409 | } 410 | 411 | // to string 412 | public function _s($val) { 413 | if ($this->isPrimitive($val)) { 414 | return (string)$val; 415 | } 416 | elseif ($val === true) { 417 | return 'true'; 418 | } 419 | elseif ($val === false) { 420 | return 'false'; 421 | } 422 | elseif (is_array($val)) { 423 | return json_encode($val); 424 | } 425 | return ''; 426 | } 427 | 428 | // call filter function 429 | public function _f($id, $args) { 430 | $filterId = '_f_' . $id; 431 | if (method_exists($this, $filterId)) { 432 | $ret = $this->$filterId($args); 433 | } 434 | else { 435 | $ret = $this->defaultFilter($args); 436 | } 437 | return $ret; 438 | } 439 | 440 | // loop the array and call the function 441 | public function _l($arr, $alias, $iterator1, $iterator2, $fn) { 442 | if (!$arr) { 443 | return; 444 | } 445 | $ret = array(); 446 | if (isset($this->_d[$alias])) { 447 | $originAlias = $this->_d[$alias]; 448 | } 449 | if (isset($this->_d[$iterator1])) { 450 | $originIterator1 = $this->_d[$iterator1]; 451 | } 452 | if (isset($this->_d[$iterator2])) { 453 | $originIterator2 = $this->_d[$iterator2]; 454 | } 455 | if (is_string($arr)) { 456 | $arr = str_split($arr); 457 | } 458 | elseif (is_numeric($arr)) { 459 | $tmp = array(); 460 | for ($i = 0; $i < $arr; $i++) { 461 | $tmp[$i] = $i + 1; 462 | } 463 | $arr = $tmp; 464 | } 465 | if (is_array($arr)) { 466 | foreach ($arr as $k => $v) { 467 | $this->_d[$iterator1] = $k; 468 | $this->_d[$alias] = $v; 469 | array_push($ret, $fn($this)); 470 | } 471 | } 472 | if (isset($originAlias)) { 473 | $this->_d[$alias] = $originAlias; 474 | } 475 | if (isset($originIterator1)) { 476 | $this->_d[$iterator1] = $originIterator1; 477 | } 478 | if (isset($originIterator2)) { 479 | $this->_d[$iterator2] = $originIterator2; 480 | } 481 | return $ret; 482 | } 483 | 484 | // render slot 485 | public function _t($name, $fallback, $props=null, $bindObject=null) { 486 | if (isset($this->scopedSlots[$name])) { 487 | $scopedSlotsFn = $this->scopedSlots[$name]; 488 | } 489 | if (isset($scopedSlotsFn) && is_array($scopedSlotsFn) && count($scopedSlotsFn) > 1) { 490 | if (!is_array($props)) { 491 | $props = array(); 492 | } 493 | if (is_array($bindObject)) { 494 | $props = $this->extendData($props, $bindObject); 495 | } 496 | $propName = $scopedSlotsFn[0]; 497 | if (isset($this->_d[$propName])) { 498 | $originData = $this->_d[$propName]; 499 | } 500 | $this->_d[$propName] = $props; 501 | $res = $scopedSlotsFn[1]($this); 502 | if (isset($originData)) { 503 | $this->_d[$propName] = $originData; 504 | } 505 | } 506 | elseif (isset($this->slots[$name])) { 507 | $res = $this->slots[$name]; 508 | } 509 | return isset($res) ? $res : $fallback; 510 | } 511 | 512 | // create empty node 513 | public function _e() { 514 | return $this->createEmptyVNode(); 515 | } 516 | 517 | // number add or string concat 518 | public function _a($a, $b) { 519 | if (!is_string($a) && is_numeric($a) && !is_string($b) && is_numeric($b)) { 520 | return $a + $b; 521 | } 522 | return $a . $b; 523 | } 524 | 525 | // loose equal 526 | public function _q($a, $b) { 527 | if (is_array($a) && is_array($b)) { 528 | if (json_encode($a) === json_encode($b)) { 529 | return true; 530 | } 531 | } 532 | elseif (!is_array($a) && !is_array($b)) { 533 | if ((string)$a === (string)$b) { 534 | return true; 535 | } 536 | } 537 | return false; 538 | } 539 | 540 | // loose indexOf 541 | public function _i($arr, $val) { 542 | foreach($arr as $i => $v) { 543 | if ($this->_q($v, $val)) { 544 | return $i; 545 | } 546 | } 547 | return -1; 548 | } 549 | 550 | // to number 551 | public function _n($val) { 552 | if (is_numeric($val)) { 553 | return $val; 554 | } 555 | elseif (is_string($val)) { 556 | $arr = str_split($val); 557 | $ret = array(); 558 | $hasDot = false; 559 | foreach ($arr as $v) { 560 | preg_match("/\d|\./", $v, $match); 561 | if (count($match) > 0 && ($v != '.' || !$hasDot)) { 562 | $ret[] = $v; 563 | if ($v === '.') { 564 | $hasDot = true; 565 | } 566 | } 567 | else { 568 | break; 569 | } 570 | } 571 | $ret = join('', $ret); 572 | return floatval($ret); 573 | } 574 | return $val; 575 | } 576 | 577 | // get length 578 | public function _len($val) { 579 | if (is_string($val)) { 580 | return strlen($val); 581 | } 582 | elseif ($this->isPlainArray($val)) { 583 | return count($val); 584 | } 585 | else { 586 | return $val['length']; 587 | } 588 | } 589 | 590 | public function isUnaryTag($tag) { 591 | $arr = array( 592 | 'area','base','br','col','embed','frame','hr','img','input','isindex','keygen','link','meta','param','source','track','wbr' 593 | ); 594 | return in_array(strtolower($tag), $arr); 595 | } 596 | 597 | // html 598 | private $html = ''; 599 | 600 | // css 601 | private $css = array(); 602 | 603 | // _render 604 | public function _render($ctx) {} 605 | 606 | // render 607 | public function render($data = null) { 608 | if (isset($data) && is_array($data)) { 609 | $this->_d = array_merge_recursive($data, $this->_d); 610 | } 611 | $vnode = $this->_render($this); 612 | if ($vnode instanceof VNode) { 613 | $this->getRenderCnt($vnode, true); 614 | } 615 | return array( 616 | 'html' => $this->html, 617 | 'css' => join('', array_unique($this->css)) 618 | ); 619 | } 620 | 621 | public function getRenderCnt($virtualDom, $isRoot=false) { 622 | if (isset($virtualDom->context->style)) { 623 | $this->css[] = $virtualDom->context->style; 624 | } 625 | if ($virtualDom->tag) { 626 | if ($virtualDom->isComponent) { 627 | $children = $virtualDom->children[0]; 628 | // 合并staticClass 629 | $staticClass = array(); 630 | if (isset($children->data['staticClass'])){ 631 | array_push($staticClass, $children->data['staticClass']); 632 | } 633 | if (isset($virtualDom->data['staticClass'])) { 634 | array_push($staticClass, $virtualDom->data['staticClass']); 635 | } 636 | $staticClass = join(' ', $staticClass); 637 | if ($staticClass != '') { 638 | $children->data['staticClass'] = $staticClass; 639 | } 640 | // 合并class 641 | $klass = array(); 642 | if (isset($children->data['class'])) { 643 | $klass = $this->handleClass($children->data['class']); 644 | } 645 | if (isset($virtualDom->data['class'])) { 646 | $klass = array_merge($klass, $this->handleClass($virtualDom->data['class'])); 647 | } 648 | if (count($klass) > 0) { 649 | $children->data['class'] = $klass; 650 | } 651 | $virtualDom = $children; 652 | } 653 | if ($isRoot) { 654 | if (!isset($virtualDom->data) || !is_array($virtualDom->data)) { 655 | $virtualDom->data = array(); 656 | } 657 | if (!isset($virtualDom->data['attrs']) || !is_array($virtualDom->data['attrs'])) { 658 | $virtualDom->data['attrs'] = array(); 659 | } 660 | $virtualDom->data['attrs']['server-rendered'] = 'true'; 661 | } 662 | $this->html .= $this->renderStartingTag($virtualDom); 663 | 664 | $children = $virtualDom->children; 665 | if (is_array($children) && count($children) > 0) { 666 | foreach ($children as $v) { 667 | $this->getRenderCnt($v); 668 | } 669 | } 670 | 671 | $tag = $virtualDom->tag; 672 | if (!$this->isUnaryTag($tag)) { 673 | $this->html .= ''; 674 | } 675 | } 676 | elseif ($virtualDom->skipToChildren) { 677 | $children = $virtualDom->children; 678 | if (is_array($children) && count($children) > 0) { 679 | foreach ($children as $v) { 680 | $this->getRenderCnt($v); 681 | } 682 | } 683 | } 684 | elseif ($virtualDom->isComment) { 685 | $this->html .= ""; 686 | } 687 | else { 688 | $this->html .= $virtualDom->raw ? $virtualDom->text : htmlspecialchars($virtualDom->text, ENT_QUOTES); 689 | } 690 | } 691 | 692 | // 是否是布尔值的属性 693 | public function isBooleanAttr($attr) { 694 | $allAttrs = array( 695 | 'allowfullscreen','async','autofocus','autoplay','checked','compact','controls','declare', 696 | 'default','defaultchecked','defaultmuted','defaultselected','defer','disabled', 697 | 'enabled','formnovalidate','hidden','indeterminate','inert','ismap','itemscope','loop','multiple', 698 | 'muted','nohref','noresize','noshade','novalidate','nowrap','open','pauseonexit','readonly', 699 | 'required','reversed','scoped','seamless','selected','sortable','translate', 700 | 'truespeed','typemustmatch','visible' 701 | ); 702 | 703 | if (in_array($attr, $allAttrs)) { 704 | return true; 705 | } 706 | return false; 707 | } 708 | 709 | // 值是否是false或者跟null 710 | public function isFalsyAttrValue($val) { 711 | if ($val === null || $val === false) { 712 | return true; 713 | } 714 | return false; 715 | } 716 | 717 | // 是否可枚举值的属性 718 | public function isEnumeratedAttr($attr) { 719 | $allAttrs = array( 720 | 'contenteditable','draggable','spellcheck' 721 | ); 722 | 723 | if (in_array($attr, $allAttrs)) { 724 | return true; 725 | } 726 | return false; 727 | } 728 | 729 | // 反camelize化 730 | public function inCamelize($str) { 731 | preg_match_all('/([A-Z])/', $str, $match); 732 | foreach ($match[0] as $i => $e) { 733 | $str = str_replace($e, '-' . strtolower($match[1][$i]), $str); 734 | } 735 | return $str; 736 | } 737 | 738 | // 处理class的数据结构 739 | public function handleClass($arr) { 740 | $ret = array(); 741 | if (is_array($arr)) { 742 | if ($this->isPlainArray($arr)) { 743 | foreach ($arr as $k => $v) { 744 | if (is_string($v)) { 745 | array_push($ret, $v); 746 | } 747 | else { 748 | $ret2 = $this->handleClass($v); 749 | $ret = array_merge($ret, $ret2); 750 | } 751 | } 752 | } 753 | else { 754 | foreach ($arr as $k => $v) { 755 | if ($v) { 756 | array_push($ret, $k); 757 | } 758 | } 759 | } 760 | } 761 | elseif (is_string($arr)) { 762 | array_push($ret, $arr); 763 | } 764 | 765 | return $ret; 766 | } 767 | 768 | // 把数组的key处理成连字符形式 769 | public function handleArrayKey($arr) { 770 | $ret = array(); 771 | if (is_array($arr)) { 772 | foreach ($arr as $k => $v) { 773 | $k = $this->inCamelize($k); 774 | $ret[$k] = $v; 775 | } 776 | } 777 | 778 | return $ret; 779 | } 780 | 781 | // 处理style的数据结构 782 | public function handleStyle($arr) { 783 | $ret = array(); 784 | if (is_array($arr)) { 785 | if ($this->isPlainArray($arr)) { 786 | foreach ($arr as $v) { 787 | if (is_array($v)) { 788 | $v = $this->handleArrayKey($v); 789 | } 790 | $ret = array_merge($ret, $v); 791 | } 792 | } 793 | else { 794 | $ret = $this->handleArrayKey($arr); 795 | } 796 | } 797 | return $ret; 798 | } 799 | 800 | public function renderStartingTag(&$virtualDom) { 801 | $this->renderDomProps($virtualDom->data, $virtualDom->children); 802 | $markup = '<' . $virtualDom->tag; 803 | $data = $virtualDom->data; 804 | 805 | // 处理attrs 806 | if (isset($data['attrs']) && count($data['attrs']) > 0) { 807 | foreach ($data['attrs'] as $k => $v) { 808 | if ($this->isBooleanAttr($k)) { 809 | if (!$this->isFalsyAttrValue($v)) { 810 | $markup .= ' ' . $k . '="' . $k . '"'; 811 | } 812 | } 813 | elseif ($this->isEnumeratedAttr($k)) { 814 | $markup .= ' ' . $k . '="' . (($this->isFalsyAttrValue($v) || $v === 'false') ? 'false' : 'true') . '"'; 815 | } 816 | else { 817 | /** 818 | * php中字符串连接跟js的处理不一样 819 | * 针对不同类型的数据,我们在php中模拟js数据类型的字符串连接 820 | */ 821 | // 模拟js中的数组 822 | if ($this->isPlainArray($v)) { 823 | $v = join(',', $v); 824 | } 825 | // 模拟js中的对象 826 | elseif (is_array($v)) { 827 | $v = '[object Object]'; 828 | } 829 | // 模拟js中的true 830 | elseif ($v === true) { 831 | $v = 'true'; 832 | } 833 | // 模拟js中的false 834 | elseif ($v === false) { 835 | $v = 'false'; 836 | } 837 | // 模拟js中的null 838 | elseif ($v === null) { 839 | $v = 'null'; 840 | } 841 | 842 | $markup .= ' ' . $k . '="' . $v . '"'; 843 | } 844 | } 845 | } 846 | 847 | // 处理class 848 | $cls = array(); 849 | if (isset($data['staticClass'])) { 850 | array_push($cls, $data['staticClass']); 851 | } 852 | if (isset($data['class'])) { 853 | $cls = array_merge($cls, $this->handleClass($data['class'])); 854 | } 855 | if (count($cls) > 0) { 856 | $markup .= ' class="' . join(' ', $cls) . '"'; 857 | } 858 | 859 | // 处理directives 860 | if (isset($data['directives'])) { 861 | $dirs = $data['directives']; 862 | foreach ($dirs as $dir) { 863 | if ($dir['name'] === 'show' && !$dir['value']) { 864 | if (!$data['staticStyle']) { 865 | $data['staticStyle'] = array(); 866 | } 867 | $data['staticStyle']['display'] = 'none'; 868 | } 869 | } 870 | } 871 | 872 | // 处理style 873 | $style = array(); 874 | if (isset($data['staticStyle'])) { 875 | $style = $this->handleArrayKey($data['staticStyle']); 876 | } 877 | if (isset($data['style'])) { 878 | $style = array_merge($style, $this->handleStyle($data['style'])); 879 | } 880 | if (count($style) > 0) { 881 | $markup .= ' style="'; 882 | foreach ($style as $k => $v) { 883 | $markup .= $k . ':' . $v . ';'; 884 | } 885 | //$markup = substr($markup, 0, -1); 886 | $markup .= '"'; 887 | } 888 | 889 | $markup .= '>'; 890 | 891 | return $markup; 892 | } 893 | 894 | // 是否合法属性 895 | public function isValidAttr($attr) { 896 | $allAttrs = array( 897 | 'accept','accept-charset','accesskey','action','align','alt','async','autocomplete', 898 | 'autofocus','autoplay','autosave','bgcolor','border','buffered','challenge','charset', 899 | 'checked','cite','class','code','codebase','color','cols','colspan','content','http-equiv', 900 | 'name','contenteditable','contextmenu','controls','coords','data','datetime','default', 901 | 'defer','dir','dirname','disabled','download','draggable','dropzone','enctype','method','for', 902 | 'form','formaction','headers','height','hidden','high','href','hreflang','http-equiv', 903 | 'icon','id','ismap','itemprop','keytype','kind','label','lang','language','list','loop','low', 904 | 'manifest','max','maxlength','media','method','GET','POST','min','multiple','email','file', 905 | 'muted','name','novalidate','open','optimum','pattern','ping','placeholder','poster', 906 | 'preload','radiogroup','readonly','rel','required','reversed','rows','rowspan','sandbox', 907 | 'scope','scoped','seamless','selected','shape','size','type','text','password','sizes','span', 908 | 'spellcheck','src','srcdoc','srclang','srcset','start','step','style','summary','tabindex', 909 | 'target','title','type','usemap','value','width','wrap' 910 | ); 911 | 912 | if (in_array($attr, $allAttrs) || strpos($attr, 'data-') === 0 || strpos($attr, 'aria-') === 0) { 913 | return true; 914 | } 915 | return false; 916 | } 917 | 918 | // 处理vnode的domProps属性 919 | public function renderDomProps(&$data, &$children) { 920 | if (isset($data['domProps'])) { 921 | $props = $data['domProps']; 922 | } 923 | if (isset($props) && is_array($props) && count($props) > 0) { 924 | if (isset($data['attrs'])) { 925 | $attrs = $data['attrs']; 926 | } 927 | foreach ($props as $k => $v) { 928 | if ($k === 'innerHTML') { 929 | $child = $this->generateVNode(null, null, null, $v); 930 | $child->raw = true; 931 | $children = array($child); 932 | } 933 | elseif ($k === 'textContent') { 934 | $child = $this->generateVNode(null, null, null, $v); 935 | $children = array($child); 936 | } 937 | else { 938 | $propsToAttrMap = array( 939 | 'acceptCharset' => 'accept-charset', 940 | 'className' => 'class', 941 | 'htmlFor' => 'for', 942 | 'httpEquiv' => 'http-equiv' 943 | ); 944 | if (isset($propsToAttrMap[$k])) { 945 | $attr = $propsToAttrMap[$k]; 946 | } 947 | if (!isset($attr)) { 948 | $attr = strtolower($k); 949 | } 950 | if (isset($attr) && $this->isValidAttr($attr) && !(isset($attrs) && isset($attrs[$attr]))) { 951 | if (!isset($attrs)) { 952 | $data['attrs'] = array(); 953 | } 954 | $data['attrs'][$attr] = $v; 955 | } 956 | } 957 | } 958 | } 959 | } 960 | } -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path = require('path'); 3 | var child = require('child_process'); 4 | var colors = require('colors'); 5 | 6 | var componentsPath = path.resolve(__dirname, 'components'); 7 | var components = fs.readdirSync(componentsPath); 8 | 9 | components.forEach(function (e, i) { 10 | child.exec('node index.js ' + e, (err, stdout, stderr) => { 11 | if (err || stderr) { 12 | console.log((err || stderr).red); 13 | } 14 | else { 15 | console.log(('组件 ' + e + ' 编译成功').green); 16 | } 17 | }); 18 | }); -------------------------------------------------------------------------------- /components/vue-com-test/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 21 | 22 | 23 | { 24 | "data": { 25 | "data": "hello vue", 26 | "number": 2 27 | }, 28 | "components": { 29 | "vue-com": "../vue-com" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /components/vue-com/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 17 | 18 | 23 | 24 | 25 | { 26 | "data": { 27 | "a": 123, 28 | "b": false, 29 | "items": [ 30 | 123, 31 | 456 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /components/vue-dir/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 17 | 18 | 19 | { 20 | "data": { 21 | "seen": false, 22 | "url": "http://www.baidu.com" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /components/vue-event/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 20 | 21 | 22 | { 23 | "data": { 24 | "counter": 0 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /components/vue-filter/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /components/vue-for/index.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 32 | 33 | 34 | { 35 | "data": { 36 | "styleObject": { 37 | "color": "red", 38 | "fontSize": "13px" 39 | }, 40 | "items": [ 41 | { 42 | "message": "Foo" 43 | }, 44 | { 45 | "message": "Bar" 46 | } 47 | ] 48 | }, 49 | "components": { 50 | "vue-com": "../vue-com" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /components/vue-form/index.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 49 | 50 | 51 | { 52 | "data": { 53 | "a": "hoho", 54 | "b": "haha", 55 | "checked": false, 56 | "picked": "", 57 | "selected": "", 58 | "toggle": "", 59 | "msg": "" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /components/vue-if/index.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 37 | 38 | 39 | { 40 | "data": { 41 | "ok": true, 42 | "type": "B" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /components/vue-klass/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | 14 | 15 | { 16 | "data": { 17 | "isActive": true, 18 | "classObj": { 19 | "isActive": true 20 | }, 21 | "activeClass": "active", 22 | "errorClass": "text-danger" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /components/vue-shorthand/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | 17 | 18 | { 19 | "data": { 20 | "url": "http://www.baidu.com" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /components/vue-style/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 14 | 15 | 16 | { 17 | "data": { 18 | "activeColor": "red", 19 | "fontSize": 30, 20 | "styleObject": { 21 | "color": "red", 22 | "fontSize": "13px" 23 | }, 24 | "baseStyles": { 25 | "color": "red", 26 | "fontSize": "13px" 27 | }, 28 | "overridingStyles": { 29 | "color": "green" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /components/vue-transition/index.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 31 | 32 | 33 | { 34 | "data": { 35 | "items": [ 36 | { 37 | "message": "Foo" 38 | }, 39 | { 40 | "message": "Bar" 41 | } 42 | ] 43 | }, 44 | "components": { 45 | "vue-com": "../vue-com" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /components/vue-val/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 21 | 22 | 25 | 26 | 27 | { 28 | "data": { 29 | "a": "hoho", 30 | "rawHtml": "
vue
", 31 | "dynamicId": "myid", 32 | "someDynamicCondition": false, 33 | "number": 2, 34 | "ok": true 35 | } 36 | } 37 |
-------------------------------------------------------------------------------- /imgs/reason.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joe3Ray/vue-php/f9240e17195065ec32a72b0e26b73b23333f3d36/imgs/reason.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var vuetojs = require('vue-to-js'); 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | 5 | var compName = process.argv[2]; 6 | var compPath = path.resolve(__dirname, 'components', compName, 'index.vue'); 7 | var blocks = vuetojs.getBlocks(compPath); 8 | 9 | var config; 10 | try { 11 | config = JSON.parse(blocks.config[0].code); 12 | } 13 | catch (e) { 14 | config = {}; 15 | } 16 | 17 | var configData = config.data ? jsObjToPhpArr(config.data) : 'array()'; 18 | var configComponents = config.components ? jsObjToPhpArr(config.components) : 'array()'; 19 | 20 | function jsObjToPhpArr(obj) { 21 | var ret = 'array('; 22 | var keys = Object.keys(obj); 23 | keys.forEach(function (e, i) { 24 | var val = obj[e]; 25 | ret += '"' + e + '" => '; 26 | if (typeof val === 'string') { 27 | val = val.replace(/(["'])/g, '\\$1'); 28 | ret += '"' + val + '"'; 29 | } 30 | else if (typeof val === 'object') { 31 | ret += jsObjToPhpArr(val); 32 | } 33 | else { 34 | ret += val; 35 | } 36 | if (i + 1 < keys.length) { 37 | ret += ','; 38 | } 39 | }); 40 | ret += ')'; 41 | return ret; 42 | } 43 | 44 | function jsObjToStr(obj, fn) { 45 | fn = fn || function (str) {return "'" + str + "'";}; 46 | var ret = ''; 47 | if (Array.isArray(obj)) { 48 | ret = '['; 49 | obj.forEach(function (e, i) { 50 | if (typeof e === 'object') { 51 | ret += jsObjToStr(e); 52 | } 53 | else { 54 | ret += fn(e); 55 | } 56 | if (i + 1 < obj.length) { 57 | ret += ','; 58 | } 59 | }); 60 | ret += ']'; 61 | } 62 | else { 63 | ret = '{'; 64 | var keys = Object.keys(obj); 65 | keys.forEach(function (e, i) { 66 | var val = obj[e]; 67 | ret += '"' + e + '": '; 68 | if (typeof val === 'object') { 69 | ret += jsObjToStr(val); 70 | } 71 | else { 72 | ret += fn(val); 73 | } 74 | if (i + 1 < keys.length) { 75 | ret += ','; 76 | } 77 | }); 78 | ret += '}'; 79 | } 80 | return ret; 81 | } 82 | 83 | var template = blocks.template.code; 84 | var phpCompiler = require('./vue-template-php-compiler/build'); 85 | var phpInfo = phpCompiler.compile(template); 86 | 87 | var styleCode = blocks.styles.map(function (e, i) { 88 | return e.code; 89 | }).join('').replace(/\n/g, ''); 90 | 91 | var phpRender = phpInfo.render; 92 | var phpStaticRender = phpInfo.staticRenderFns; 93 | 94 | var className = compName.replace(/-/g, '_'); 95 | className = className.split('_').map(function (e, i) { 96 | var chars = e.split(''); 97 | chars[0] = chars[0].toUpperCase(); 98 | return chars.join(''); 99 | }).join('_'); 100 | 101 | var phpCode = ` ${configComponents} 109 | ); 110 | public $style = "${styleCode}"; 111 | ${phpStaticRender.map(function (fn, i) { 112 | return ` 113 | public function _m${i}($ctx) { 114 | ${fn} 115 | } 116 | `; 117 | }).join('\n')} 118 | 119 | public function _render($ctx) { 120 | ${phpRender} 121 | } 122 | } 123 | `; 124 | 125 | var phpPath = path.resolve(__dirname, 'components', compName, 'index.php'); 126 | fs.writeFileSync(phpPath, phpCode); 127 | 128 | var transform = require('babel-core').transform; 129 | 130 | function getScript(code) { 131 | var result = transform(code, { 132 | plugins: ['transform-es2015-modules-commonjs'], 133 | presets: ['env'] 134 | }); 135 | 136 | return result.code; 137 | } 138 | 139 | var jsData = config.data ? jsObjToStr(config.data, function (str) { 140 | if (typeof str === 'string') { 141 | return "'" + str + "'"; 142 | } 143 | else { 144 | return str; 145 | } 146 | }) : '{}'; 147 | var jsComponents = config.components ? jsObjToStr(config.components, function (str) { 148 | var filepath = str; 149 | return 'require(\'' + filepath + '\')'; 150 | }) : '{}'; 151 | var jsCode = ` 152 | var _module1 = { 153 | exports: {} 154 | }; 155 | (function (module, exports) { 156 | ${getScript(blocks.script.code)} 157 | })(_module1, _module1.exports); 158 | 159 | var obj = _module1.exports.default || _module1.exports; 160 | 161 | obj.data = function () { 162 | return ${jsData}; 163 | }; 164 | obj.components = ${jsComponents}; 165 | obj.template = \`${template}\`; 166 | 167 | module.exports = obj; 168 | `; 169 | 170 | var jsPath = path.resolve(__dirname, 'components', compName, 'index.js'); 171 | fs.writeFileSync(jsPath, jsCode); -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | setComponentsDir('./components'); 20 | $info = $instance->render(); 21 | echo $info['html']; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-php", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "node build.js", 8 | "test": "mocha", 9 | "all": "npm run build && npm test" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/Joe3Ray/vue-php.git" 14 | }, 15 | "author": "", 16 | "license": "ISC", 17 | "bugs": { 18 | "url": "https://github.com/Joe3Ray/vue-php/issues" 19 | }, 20 | "homepage": "https://github.com/Joe3Ray/vue-php#readme", 21 | "dependencies": { 22 | "vue-to-js": "^0.9.3" 23 | }, 24 | "devDependencies": { 25 | "babel-core": "^6.23.1", 26 | "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", 27 | "babel-preset-env": "^1.2.1", 28 | "chai": "^3.5.0", 29 | "colors": "^1.1.2", 30 | "mocha": "^3.2.0", 31 | "vue": "2.2.4", 32 | "vue-server-renderer": "2.2.4" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | 3 | global.Vue = require('vue'); 4 | Vue.filter('lower', function (value) { 5 | return value.toLowerCase(); 6 | }); 7 | 8 | var renderer = require('vue-server-renderer/build').createRenderer(); 9 | 10 | function jsRenderer(app) { 11 | return new Promise((resolve, reject) => { 12 | renderer.renderToString(app, (err, html) => { 13 | if (err) { 14 | reject('js render error'); 15 | } 16 | else { 17 | html = html.trim(); 18 | resolve(html); 19 | } 20 | }); 21 | }); 22 | } 23 | 24 | var child = require('child_process'); 25 | 26 | function phpRenderer(name) { 27 | return new Promise((resolve, reject) => { 28 | child.exec('php -n index.php ' + name, (err, stdout, stderr) => { 29 | if (!err && !stderr) { 30 | var html = stdout.trim(); 31 | resolve(html); 32 | } 33 | else { 34 | reject('php render error'); 35 | } 36 | }); 37 | }); 38 | } 39 | 40 | function generateApp(name) { 41 | var opt = { 42 | template: '<' + name + ' />', 43 | components: {} 44 | }; 45 | opt.components[name] = require('./components/' + name); 46 | var app = new Vue(opt); 47 | return app; 48 | } 49 | 50 | describe('插值: vue-val', function () { 51 | it('vue-php对插值的渲染结果与vue-server-renderer一致', function () { 52 | var app = generateApp('vue-val'); 53 | return Promise.all([jsRenderer(app), phpRenderer('vue-val')]).then(([res1, res2]) => { 54 | expect(res1).to.equal(res2); 55 | }) 56 | }); 57 | }); 58 | 59 | describe('指令: vue-dir', function () { 60 | it('vue-php对指令的渲染结果与vue-server-renderer一致', function () { 61 | var app = generateApp('vue-dir'); 62 | return Promise.all([jsRenderer(app), phpRenderer('vue-dir')]).then(([res1, res2]) => { 63 | expect(res1).to.equal(res2); 64 | }) 65 | }); 66 | }); 67 | 68 | describe('过滤器: vue-filter', function () { 69 | it('vue-php对过滤器的渲染结果与vue-server-renderer一致', function () { 70 | var app = generateApp('vue-filter'); 71 | return Promise.all([jsRenderer(app), phpRenderer('vue-filter')]).then(([res1, res2]) => { 72 | expect(res1).to.equal(res2); 73 | }) 74 | }); 75 | }); 76 | 77 | describe('v-bind/v-on简写: vue-shorthand', function () { 78 | it('vue-php对v-bind/v-on简写的渲染结果与vue-server-renderer一致', function () { 79 | var app = generateApp('vue-shorthand'); 80 | return Promise.all([jsRenderer(app), phpRenderer('vue-shorthand')]).then(([res1, res2]) => { 81 | expect(res1).to.equal(res2); 82 | }) 83 | }); 84 | }); 85 | 86 | describe('css类: vue-klass', function () { 87 | it('vue-php对css类的渲染结果与vue-server-renderer一致', function () { 88 | var app = generateApp('vue-klass'); 89 | return Promise.all([jsRenderer(app), phpRenderer('vue-klass')]).then(([res1, res2]) => { 90 | expect(res1).to.equal(res2); 91 | }) 92 | }); 93 | }); 94 | 95 | describe('内联style: vue-style', function () { 96 | it('vue-php对内联style的渲染结果与vue-server-renderer一致', function () { 97 | var app = generateApp('vue-style'); 98 | return Promise.all([jsRenderer(app), phpRenderer('vue-style')]).then(([res1, res2]) => { 99 | expect(res1).to.equal(res2); 100 | }) 101 | }); 102 | }); 103 | 104 | describe('条件判断: vue-if', function () { 105 | it('vue-php对条件判断的渲染结果与vue-server-renderer一致', function () { 106 | var app = generateApp('vue-if'); 107 | return Promise.all([jsRenderer(app), phpRenderer('vue-if')]).then(([res1, res2]) => { 108 | expect(res1).to.equal(res2); 109 | }) 110 | }); 111 | }); 112 | 113 | describe('循环: vue-for', function () { 114 | it('vue-php对循环的渲染结果与vue-server-renderer一致', function () { 115 | var app = generateApp('vue-for'); 116 | return Promise.all([jsRenderer(app), phpRenderer('vue-for')]).then(([res1, res2]) => { 117 | expect(res1).to.equal(res2); 118 | }) 119 | }); 120 | }); 121 | 122 | describe('事件: vue-event', function () { 123 | it('vue-php对事件的渲染结果与vue-server-renderer一致', function () { 124 | var app = generateApp('vue-event'); 125 | return Promise.all([jsRenderer(app), phpRenderer('vue-event')]).then(([res1, res2]) => { 126 | expect(res1).to.equal(res2); 127 | }) 128 | }); 129 | }); 130 | 131 | describe('表单: vue-form', function () { 132 | it('vue-php对表单的渲染结果与vue-server-renderer一致', function () { 133 | var app = generateApp('vue-form'); 134 | return Promise.all([jsRenderer(app), phpRenderer('vue-form')]).then(([res1, res2]) => { 135 | expect(res1).to.equal(res2); 136 | }) 137 | }); 138 | }); 139 | 140 | describe('组件: vue-com', function () { 141 | it('vue-php对组件的渲染结果与vue-server-renderer一致', function () { 142 | var app = generateApp('vue-com-test'); 143 | return Promise.all([jsRenderer(app), phpRenderer('vue-com-test')]).then(([res1, res2]) => { 144 | expect(res1).to.equal(res2); 145 | }) 146 | }); 147 | }); 148 | 149 | describe('过渡动画: vue-transition', function () { 150 | it('vue-php对过渡动画的渲染结果与vue-server-renderer一致', function () { 151 | var app = generateApp('vue-transition'); 152 | return Promise.all([jsRenderer(app), phpRenderer('vue-transition')]).then(([res1, res2]) => { 153 | expect(res1).to.equal(res2); 154 | }) 155 | }); 156 | }); -------------------------------------------------------------------------------- /vue-template-php-compiler/README.md: -------------------------------------------------------------------------------- 1 | # vue-template-compiler 2 | 3 | > This package is auto-generated. For pull requests please see [src/entries/web-compiler.js](https://github.com/vuejs/vue/blob/dev/src/entries/web-compiler.js). 4 | 5 | This package can be used to pre-compile Vue 2.0 templates into render functions to avoid runtime-compilation overhead and CSP restrictions. You will only need it if you are writing build tools with very specific needs. In most cases you should be using [vue-loader](https://github.com/vuejs/vue-loader) or [vueify](https://github.com/vuejs/vueify) instead, both of which use this package internally. 6 | 7 | ## Installation 8 | 9 | ``` bash 10 | npm install vue-template-compiler 11 | ``` 12 | 13 | ``` js 14 | const compiler = require('vue-template-compiler') 15 | ``` 16 | 17 | ## API 18 | 19 | ### compiler.compile(template, [options]) 20 | 21 | Compiles a template string and returns compiled JavaScript code. The returned result is an object of the following format: 22 | 23 | ``` js 24 | { 25 | ast: ?ASTElement, // parsed template elements to AST 26 | render: string, // main render function code 27 | staticRenderFns: Array, // render code for static sub trees, if any 28 | errors: Array // template syntax errors, if any 29 | } 30 | ``` 31 | 32 | Note the returned function code uses `with` and thus cannot be used in strict mode code. 33 | 34 | #### Options 35 | 36 | It's possible to hook into the compilation process to support custom template features. **However, beware that by injecting custom compile-time modules, your templates will not work with other build tools built on standard built-in modules, e.g `vue-loader` and `vueify`.** 37 | 38 | The optional `options` object can contain the following: 39 | 40 | - `modules` 41 | 42 | An array of compiler modules. For details on compiler modules, refer to the `ModuleOptions` type in [flow declarations](https://github.com/vuejs/vue/blob/dev/flow/compiler.js) and the [built-in modules](https://github.com/vuejs/vue/tree/dev/src/platforms/web/compiler/modules). 43 | 44 | - `directives` 45 | 46 | An object where the key is the directive name and the value is a function that transforms an template AST node. For example: 47 | 48 | ``` js 49 | compiler.compile('
', { 50 | directives: { 51 | test (node, directiveMeta) { 52 | // transform node based on directiveMeta 53 | } 54 | }) 55 | ``` 56 | 57 | By default, a compile-time directive will extract the directive and the directive will not be present at runtime. If you want the directive to also be handled by a runtime definition, return `true` in the transform function. 58 | 59 | Refer to the implementation of some [built-in compile-time directives](https://github.com/vuejs/vue/tree/dev/src/platforms/web/compiler/directives). 60 | 61 | - `preserveWhitespace` 62 | 63 | Defaults to `true`. This means the compiled render function respects all the whitespaces between HTML tags. If set to `false`, all whitespaces between tags will be ignored. This can result in slightly better performance but may affect layout for inline elements. 64 | 65 | --- 66 | 67 | ### compiler.compileToFunctions(template) 68 | 69 | Similar to `compiler.compile`, but directly returns instantiated functions: 70 | 71 | ``` js 72 | { 73 | render: Function, 74 | staticRenderFns: Array 75 | } 76 | ``` 77 | 78 | This is only useful at runtime with pre-configured builds, so it doesn't accept any compile-time options. In addition, this method uses `new Function()` so it is not CSP-compliant. 79 | 80 | --- 81 | 82 | ### compiler.parseComponent(file, [options]) 83 | 84 | Parse a SFC (single-file component, or `*.vue` file) into a descriptor (refer to the `SFCDescriptor` type in [flow declarations](https://github.com/vuejs/vue/blob/dev/flow/compiler.js)). This is used in SFC build tools like `vue-loader` and `vueify`. 85 | 86 | #### Options 87 | 88 | #### `pad` 89 | 90 | `pad` is useful when you are piping the extracted content into other pre-processors, as you will get correct line numbers or character indices if there are any syntax errors. 91 | 92 | - with `{ pad: "line" }`, the extracted content for each block will be prefixed with one newline for each line in the leading content from the original file to ensure that the line numbers align with the original file. 93 | - with `{ pad: "space" }`, the extracted content for each block will be prefixed with one space for each character in the leading content from the original file to ensure that the character count remains the same as the original file. -------------------------------------------------------------------------------- /vue-template-php-compiler/index.js: -------------------------------------------------------------------------------- 1 | try { 2 | var vueVersion = require('vue').version 3 | } catch (e) {} 4 | 5 | var packageName = require('./package.json').name 6 | var packageVersion = require('./package.json').version 7 | if (vueVersion && vueVersion !== packageVersion) { 8 | throw new Error( 9 | '\n\nVue packages version mismatch:\n\n' + 10 | '- vue@' + vueVersion + '\n' + 11 | '- ' + packageName + '@' + packageVersion + '\n\n' + 12 | 'This may cause things to work incorrectly. Make sure to use the same version for both.\n' + 13 | 'If you are using vue-loader@>=10.0, simply update vue-template-compiler.\n' + 14 | 'If you are using vue-loader@<10.0 or vueify, re-installing vue-loader/vueify should bump ' + packageName + ' to the latest.\n' 15 | ) 16 | } 17 | 18 | module.exports = require('./build') 19 | -------------------------------------------------------------------------------- /vue-template-php-compiler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_args": [ 3 | [ 4 | "vue-template-compiler", 5 | "/Users/zhulei/Documents/demo/vue-to-js" 6 | ] 7 | ], 8 | "_from": "vue-template-compiler@latest", 9 | "_id": "vue-template-compiler@2.2.4", 10 | "_inCache": true, 11 | "_installable": true, 12 | "_location": "/vue-template-compiler", 13 | "_nodeVersion": "7.4.0", 14 | "_npmOperationalInternal": { 15 | "host": "packages-18-east.internal.npmjs.com", 16 | "tmp": "tmp/vue-template-compiler-2.2.4.tgz_1489417683896_0.9300020763184875" 17 | }, 18 | "_npmUser": { 19 | "email": "yyx990803@gmail.com", 20 | "name": "yyx990803" 21 | }, 22 | "_npmVersion": "4.0.5", 23 | "_phantomChildren": {}, 24 | "_requested": { 25 | "name": "vue-template-compiler", 26 | "raw": "vue-template-compiler", 27 | "rawSpec": "", 28 | "scope": null, 29 | "spec": "latest", 30 | "type": "tag" 31 | }, 32 | "_requiredBy": [ 33 | "/" 34 | ], 35 | "_resolved": "http://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.2.4.tgz", 36 | "_shasum": "2856fb09f1571e9098872bf3c512d670eeeafba9", 37 | "_shrinkwrap": null, 38 | "_spec": "vue-template-compiler", 39 | "_where": "/Users/zhulei/Documents/demo/vue-to-js", 40 | "author": { 41 | "name": "Evan You" 42 | }, 43 | "bugs": { 44 | "url": "https://github.com/vuejs/vue/issues" 45 | }, 46 | "dependencies": { 47 | "de-indent": "^1.0.2", 48 | "he": "^1.1.0" 49 | }, 50 | "description": "template compiler for Vue 2.0", 51 | "devDependencies": {}, 52 | "directories": {}, 53 | "dist": { 54 | "shasum": "2856fb09f1571e9098872bf3c512d670eeeafba9", 55 | "tarball": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.2.4.tgz" 56 | }, 57 | "homepage": "https://github.com/vuejs/vue/tree/dev/packages/vue-template-compiler#readme", 58 | "keywords": [ 59 | "vue", 60 | "compiler" 61 | ], 62 | "license": "MIT", 63 | "main": "index.js", 64 | "maintainers": [ 65 | { 66 | "email": "yyx990803@gmail.com", 67 | "name": "yyx990803" 68 | } 69 | ], 70 | "name": "vue-template-compiler", 71 | "optionalDependencies": {}, 72 | "readme": "ERROR: No README data found!", 73 | "repository": { 74 | "type": "git", 75 | "url": "git+https://github.com/vuejs/vue.git" 76 | }, 77 | "scripts": {}, 78 | "version": "2.2.4" 79 | } 80 | -------------------------------------------------------------------------------- /vue-template-php-compiler/vuescript.pegjs: -------------------------------------------------------------------------------- 1 | // JavaScript Grammar 2 | // ================== 3 | // 4 | // Based on grammar from ECMA-262, 5.1 Edition [1]. Generated parser builds a 5 | // syntax tree compatible with the ESTree spec [2]. 6 | // 7 | // Limitations: 8 | // 9 | // * Non-BMP characters are completely ignored to avoid surrogate pair 10 | // handling. 11 | // 12 | // * One can create identifiers containing illegal characters using Unicode 13 | // escape sequences. For example, "abcd\u0020efgh" is not a valid 14 | // identifier, but it is accepted by the parser. 15 | // 16 | // * Strict mode is not recognized. This means that within strict mode code, 17 | // "implements", "interface", "let", "package", "private", "protected", 18 | // "public", "static" and "yield" can be used as names. Many other 19 | // restrictions and exceptions from Annex C are also not applied. 20 | // 21 | // All the limitations could be resolved, but the costs would likely outweigh 22 | // the benefits. 23 | // 24 | // Many thanks to inimino [3] for his grammar [4] which helped me to solve some 25 | // problems (such as automatic semicolon insertion) and also served to double 26 | // check that I converted the original grammar correctly. 27 | // 28 | // [1] http://www.ecma-international.org/publications/standards/Ecma-262.htm 29 | // [2] https://github.com/estree/estree 30 | // [3] http://inimino.org/~inimino/blog/ 31 | // [4] http://boshi.inimino.org/3box/asof/1270029991384/PEG/ECMAScript_unified.peg 32 | 33 | { 34 | var TYPES_TO_PROPERTY_NAMES = { 35 | CallExpression: "callee", 36 | MemberExpression: "object", 37 | }; 38 | 39 | function filledArray(count, value) { 40 | return Array.apply(null, new Array(count)) 41 | .map(function() { return value; }); 42 | } 43 | 44 | function extractOptional(optional, index) { 45 | return optional ? optional[index] : null; 46 | } 47 | 48 | function extractList(list, index) { 49 | return list.map(function(element) { return element[index]; }); 50 | } 51 | 52 | function buildList(head, tail, index) { 53 | return [head].concat(extractList(tail, index)); 54 | } 55 | 56 | function buildBinaryExpression(head, tail) { 57 | return tail.reduce(function(result, element) { 58 | return { 59 | type: "BinaryExpression", 60 | operator: element[1], 61 | left: result, 62 | right: element[3] 63 | }; 64 | }, head); 65 | } 66 | 67 | function buildLogicalExpression(head, tail) { 68 | return tail.reduce(function(result, element) { 69 | return { 70 | type: "LogicalExpression", 71 | operator: element[1], 72 | left: result, 73 | right: element[3] 74 | }; 75 | }, head); 76 | } 77 | 78 | function optionalList(value) { 79 | return value !== null ? value : []; 80 | } 81 | } 82 | 83 | Start 84 | = __ expression:VueExpression __ { return expression; } 85 | 86 | VueExpression 87 | = head:VueExpressionNoFilter 88 | tail:( 89 | __ "|" __ filter:VueFilter { 90 | return filter; 91 | } 92 | )* 93 | { 94 | return { 95 | type: "VueExpression", 96 | expression: head, 97 | filters: tail 98 | }; 99 | } 100 | 101 | VueExpressionNoFilter 102 | = ConditionalExpressionNoIn 103 | 104 | VueFilter 105 | = callee:Identifier __ args:Arguments { 106 | return { type: "VueFilter", name: callee.name, arguments: args }; 107 | } 108 | / callee:Identifier { 109 | return { type: "VueFilter", name: callee.name, arguments: [] }; 110 | } 111 | 112 | // Start 113 | // = __ program:Program __ { return program; } 114 | 115 | // ----- A.1 Lexical Grammar ----- 116 | 117 | SourceCharacter 118 | = . 119 | 120 | WhiteSpace "whitespace" 121 | = "\t" 122 | / "\v" 123 | / "\f" 124 | / " " 125 | / "\u00A0" 126 | / "\uFEFF" 127 | / Zs 128 | 129 | LineTerminator 130 | = [\n\r\u2028\u2029] 131 | 132 | LineTerminatorSequence "end of line" 133 | = "\n" 134 | / "\r\n" 135 | / "\r" 136 | / "\u2028" 137 | / "\u2029" 138 | // 139 | // Comment "comment" 140 | // = MultiLineComment 141 | // / SingleLineComment 142 | // 143 | // MultiLineComment 144 | // = "/*" (!"*/" SourceCharacter)* "*/" 145 | // 146 | // MultiLineCommentNoLineTerminator 147 | // = "/*" (!("*/" / LineTerminator) SourceCharacter)* "*/" 148 | // 149 | // SingleLineComment 150 | // = "//" (!LineTerminator SourceCharacter)* 151 | // 152 | Identifier 153 | = !ReservedWord name:IdentifierName { return name; } 154 | 155 | IdentifierName "identifier" 156 | = head:IdentifierStart tail:IdentifierPart* { 157 | return { 158 | type: "Identifier", 159 | name: head + tail.join("") 160 | }; 161 | } 162 | 163 | IdentifierStart 164 | = UnicodeLetter 165 | / "$" 166 | / "_" 167 | / "\\" sequence:UnicodeEscapeSequence { return sequence; } 168 | 169 | IdentifierPart 170 | = IdentifierStart 171 | / UnicodeCombiningMark 172 | / UnicodeDigit 173 | / UnicodeConnectorPunctuation 174 | / "\u200C" 175 | / "\u200D" 176 | 177 | UnicodeLetter 178 | = Lu 179 | / Ll 180 | / Lt 181 | / Lm 182 | / Lo 183 | / Nl 184 | 185 | UnicodeCombiningMark 186 | = Mn 187 | / Mc 188 | 189 | UnicodeDigit 190 | = Nd 191 | 192 | UnicodeConnectorPunctuation 193 | = Pc 194 | 195 | ReservedWord 196 | = Keyword 197 | / FutureReservedWord 198 | / NullLiteral 199 | / BooleanLiteral 200 | 201 | Keyword 202 | = BreakToken 203 | / CaseToken 204 | / CatchToken 205 | / ContinueToken 206 | / DebuggerToken 207 | / DefaultToken 208 | / DeleteToken 209 | / DoToken 210 | / ElseToken 211 | / FinallyToken 212 | / ForToken 213 | / FunctionToken 214 | / IfToken 215 | / InstanceofToken 216 | / InToken 217 | / NewToken 218 | / ReturnToken 219 | / SwitchToken 220 | / ThisToken 221 | / ThrowToken 222 | / TryToken 223 | / TypeofToken 224 | / VarToken 225 | / VoidToken 226 | / WhileToken 227 | / WithToken 228 | 229 | FutureReservedWord 230 | = ClassToken 231 | / ConstToken 232 | / EnumToken 233 | / ExportToken 234 | / ExtendsToken 235 | / ImportToken 236 | / SuperToken 237 | 238 | //*Literal 239 | //* = NullLiteral 240 | //* / BooleanLiteral 241 | //* / NumericLiteral 242 | //* / StringLiteral 243 | //* / RegularExpressionLiteral 244 | 245 | Literal 246 | = NullLiteral 247 | / BooleanLiteral 248 | / NumericLiteral 249 | / StringLiteral 250 | 251 | NullLiteral 252 | = NullToken { return { type: "Literal", value: null }; } 253 | 254 | BooleanLiteral 255 | = TrueToken { return { type: "Literal", value: true }; } 256 | / FalseToken { return { type: "Literal", value: false }; } 257 | 258 | // The "!(IdentifierStart / DecimalDigit)" predicate is not part of the official 259 | // grammar, it comes from text in section 7.8.3. 260 | NumericLiteral "number" 261 | = literal:HexIntegerLiteral !(IdentifierStart / DecimalDigit) { 262 | return literal; 263 | } 264 | / literal:DecimalLiteral !(IdentifierStart / DecimalDigit) { 265 | return literal; 266 | } 267 | 268 | DecimalLiteral 269 | = DecimalIntegerLiteral "." DecimalDigit* ExponentPart? { 270 | return { type: "Literal", value: parseFloat(text()) }; 271 | } 272 | / "." DecimalDigit+ ExponentPart? { 273 | return { type: "Literal", value: parseFloat(text()) }; 274 | } 275 | / DecimalIntegerLiteral ExponentPart? { 276 | return { type: "Literal", value: parseFloat(text()) }; 277 | } 278 | 279 | DecimalIntegerLiteral 280 | = "0" 281 | / NonZeroDigit DecimalDigit* 282 | 283 | DecimalDigit 284 | = [0-9] 285 | 286 | NonZeroDigit 287 | = [1-9] 288 | 289 | ExponentPart 290 | = ExponentIndicator SignedInteger 291 | 292 | ExponentIndicator 293 | = "e"i 294 | 295 | SignedInteger 296 | = [+-]? DecimalDigit+ 297 | 298 | HexIntegerLiteral 299 | = "0x"i digits:$HexDigit+ { 300 | return { type: "Literal", value: parseInt(digits, 16) }; 301 | } 302 | 303 | HexDigit 304 | = [0-9a-f]i 305 | 306 | StringLiteral "string" 307 | = '"' chars:DoubleStringCharacter* '"' { 308 | return { type: "Literal", value: chars.join("") }; 309 | } 310 | / "'" chars:SingleStringCharacter* "'" { 311 | return { type: "Literal", value: chars.join("") }; 312 | } 313 | 314 | DoubleStringCharacter 315 | = !('"' / "\\" / LineTerminator) SourceCharacter { return text(); } 316 | / "\\" sequence:EscapeSequence { return sequence; } 317 | / LineContinuation 318 | 319 | SingleStringCharacter 320 | = !("'" / "\\" / LineTerminator) SourceCharacter { return text(); } 321 | / "\\" sequence:EscapeSequence { return sequence; } 322 | / LineContinuation 323 | 324 | LineContinuation 325 | = "\\" LineTerminatorSequence { return ""; } 326 | 327 | EscapeSequence 328 | = CharacterEscapeSequence 329 | / "0" !DecimalDigit { return "\0"; } 330 | / HexEscapeSequence 331 | / UnicodeEscapeSequence 332 | 333 | CharacterEscapeSequence 334 | = SingleEscapeCharacter 335 | / NonEscapeCharacter 336 | 337 | SingleEscapeCharacter 338 | = "'" 339 | / '"' 340 | / "\\" 341 | / "b" { return "\b"; } 342 | / "f" { return "\f"; } 343 | / "n" { return "\n"; } 344 | / "r" { return "\r"; } 345 | / "t" { return "\t"; } 346 | / "v" { return "\v"; } 347 | 348 | NonEscapeCharacter 349 | = !(EscapeCharacter / LineTerminator) SourceCharacter { return text(); } 350 | 351 | EscapeCharacter 352 | = SingleEscapeCharacter 353 | / DecimalDigit 354 | / "x" 355 | / "u" 356 | 357 | HexEscapeSequence 358 | = "x" digits:$(HexDigit HexDigit) { 359 | return String.fromCharCode(parseInt(digits, 16)); 360 | } 361 | 362 | UnicodeEscapeSequence 363 | = "u" digits:$(HexDigit HexDigit HexDigit HexDigit) { 364 | return String.fromCharCode(parseInt(digits, 16)); 365 | } 366 | 367 | // RegularExpressionLiteral "regular expression" 368 | // = "/" pattern:$RegularExpressionBody "/" flags:$RegularExpressionFlags { 369 | // var value; 370 | // 371 | // try { 372 | // value = new RegExp(pattern, flags); 373 | // } catch (e) { 374 | // error(e.message); 375 | // } 376 | // 377 | // return { type: "Literal", value: value }; 378 | // } 379 | // 380 | // RegularExpressionBody 381 | // = RegularExpressionFirstChar RegularExpressionChar* 382 | // 383 | // RegularExpressionFirstChar 384 | // = ![*\\/[] RegularExpressionNonTerminator 385 | // / RegularExpressionBackslashSequence 386 | // / RegularExpressionClass 387 | // 388 | // RegularExpressionChar 389 | // = ![\\/[] RegularExpressionNonTerminator 390 | // / RegularExpressionBackslashSequence 391 | // / RegularExpressionClass 392 | // 393 | // RegularExpressionBackslashSequence 394 | // = "\\" RegularExpressionNonTerminator 395 | // 396 | // RegularExpressionNonTerminator 397 | // = !LineTerminator SourceCharacter 398 | // 399 | // RegularExpressionClass 400 | // = "[" RegularExpressionClassChar* "]" 401 | // 402 | // RegularExpressionClassChar 403 | // = ![\]\\] RegularExpressionNonTerminator 404 | // / RegularExpressionBackslashSequence 405 | // 406 | // RegularExpressionFlags 407 | // = IdentifierPart* 408 | // 409 | // Unicode Character Categories 410 | // 411 | // Extracted from the following Unicode Character Database file: 412 | // 413 | // http://www.unicode.org/Public/8.0.0/ucd/extracted/DerivedGeneralCategory.txt 414 | // 415 | // Unix magic used: 416 | // 417 | // grep "; $CATEGORY" DerivedGeneralCategory.txt | # Filter characters 418 | // cut -f1 -d " " | # Extract code points 419 | // grep -v '[0-9a-fA-F]\{5\}' | # Exclude non-BMP characters 420 | // sed -e 's/\.\./-/' | # Adjust formatting 421 | // sed -e 's/\([0-9a-fA-F]\{4\}\)/\\u\1/g' | # Adjust formatting 422 | // tr -d '\n' # Join lines 423 | // 424 | // ECMA-262 allows using Unicode 3.0 or later, version 8.0.0 was the latest one 425 | // at the time of writing. 426 | // 427 | // Non-BMP characters are completely ignored to avoid surrogate pair handling 428 | // (detecting surrogate pairs isn't possible with a simple character class and 429 | // other methods would degrade performance). I don't consider it a big deal as 430 | // even parsers in JavaScript engines of common browsers seem to ignore them. 431 | 432 | // Letter, Lowercase 433 | Ll = [\u0061-\u007A\u00B5\u00DF-\u00F6\u00F8-\u00FF\u0101\u0103\u0105\u0107\u0109\u010B\u010D\u010F\u0111\u0113\u0115\u0117\u0119\u011B\u011D\u011F\u0121\u0123\u0125\u0127\u0129\u012B\u012D\u012F\u0131\u0133\u0135\u0137-\u0138\u013A\u013C\u013E\u0140\u0142\u0144\u0146\u0148-\u0149\u014B\u014D\u014F\u0151\u0153\u0155\u0157\u0159\u015B\u015D\u015F\u0161\u0163\u0165\u0167\u0169\u016B\u016D\u016F\u0171\u0173\u0175\u0177\u017A\u017C\u017E-\u0180\u0183\u0185\u0188\u018C-\u018D\u0192\u0195\u0199-\u019B\u019E\u01A1\u01A3\u01A5\u01A8\u01AA-\u01AB\u01AD\u01B0\u01B4\u01B6\u01B9-\u01BA\u01BD-\u01BF\u01C6\u01C9\u01CC\u01CE\u01D0\u01D2\u01D4\u01D6\u01D8\u01DA\u01DC-\u01DD\u01DF\u01E1\u01E3\u01E5\u01E7\u01E9\u01EB\u01ED\u01EF-\u01F0\u01F3\u01F5\u01F9\u01FB\u01FD\u01FF\u0201\u0203\u0205\u0207\u0209\u020B\u020D\u020F\u0211\u0213\u0215\u0217\u0219\u021B\u021D\u021F\u0221\u0223\u0225\u0227\u0229\u022B\u022D\u022F\u0231\u0233-\u0239\u023C\u023F-\u0240\u0242\u0247\u0249\u024B\u024D\u024F-\u0293\u0295-\u02AF\u0371\u0373\u0377\u037B-\u037D\u0390\u03AC-\u03CE\u03D0-\u03D1\u03D5-\u03D7\u03D9\u03DB\u03DD\u03DF\u03E1\u03E3\u03E5\u03E7\u03E9\u03EB\u03ED\u03EF-\u03F3\u03F5\u03F8\u03FB-\u03FC\u0430-\u045F\u0461\u0463\u0465\u0467\u0469\u046B\u046D\u046F\u0471\u0473\u0475\u0477\u0479\u047B\u047D\u047F\u0481\u048B\u048D\u048F\u0491\u0493\u0495\u0497\u0499\u049B\u049D\u049F\u04A1\u04A3\u04A5\u04A7\u04A9\u04AB\u04AD\u04AF\u04B1\u04B3\u04B5\u04B7\u04B9\u04BB\u04BD\u04BF\u04C2\u04C4\u04C6\u04C8\u04CA\u04CC\u04CE-\u04CF\u04D1\u04D3\u04D5\u04D7\u04D9\u04DB\u04DD\u04DF\u04E1\u04E3\u04E5\u04E7\u04E9\u04EB\u04ED\u04EF\u04F1\u04F3\u04F5\u04F7\u04F9\u04FB\u04FD\u04FF\u0501\u0503\u0505\u0507\u0509\u050B\u050D\u050F\u0511\u0513\u0515\u0517\u0519\u051B\u051D\u051F\u0521\u0523\u0525\u0527\u0529\u052B\u052D\u052F\u0561-\u0587\u13F8-\u13FD\u1D00-\u1D2B\u1D6B-\u1D77\u1D79-\u1D9A\u1E01\u1E03\u1E05\u1E07\u1E09\u1E0B\u1E0D\u1E0F\u1E11\u1E13\u1E15\u1E17\u1E19\u1E1B\u1E1D\u1E1F\u1E21\u1E23\u1E25\u1E27\u1E29\u1E2B\u1E2D\u1E2F\u1E31\u1E33\u1E35\u1E37\u1E39\u1E3B\u1E3D\u1E3F\u1E41\u1E43\u1E45\u1E47\u1E49\u1E4B\u1E4D\u1E4F\u1E51\u1E53\u1E55\u1E57\u1E59\u1E5B\u1E5D\u1E5F\u1E61\u1E63\u1E65\u1E67\u1E69\u1E6B\u1E6D\u1E6F\u1E71\u1E73\u1E75\u1E77\u1E79\u1E7B\u1E7D\u1E7F\u1E81\u1E83\u1E85\u1E87\u1E89\u1E8B\u1E8D\u1E8F\u1E91\u1E93\u1E95-\u1E9D\u1E9F\u1EA1\u1EA3\u1EA5\u1EA7\u1EA9\u1EAB\u1EAD\u1EAF\u1EB1\u1EB3\u1EB5\u1EB7\u1EB9\u1EBB\u1EBD\u1EBF\u1EC1\u1EC3\u1EC5\u1EC7\u1EC9\u1ECB\u1ECD\u1ECF\u1ED1\u1ED3\u1ED5\u1ED7\u1ED9\u1EDB\u1EDD\u1EDF\u1EE1\u1EE3\u1EE5\u1EE7\u1EE9\u1EEB\u1EED\u1EEF\u1EF1\u1EF3\u1EF5\u1EF7\u1EF9\u1EFB\u1EFD\u1EFF-\u1F07\u1F10-\u1F15\u1F20-\u1F27\u1F30-\u1F37\u1F40-\u1F45\u1F50-\u1F57\u1F60-\u1F67\u1F70-\u1F7D\u1F80-\u1F87\u1F90-\u1F97\u1FA0-\u1FA7\u1FB0-\u1FB4\u1FB6-\u1FB7\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FC7\u1FD0-\u1FD3\u1FD6-\u1FD7\u1FE0-\u1FE7\u1FF2-\u1FF4\u1FF6-\u1FF7\u210A\u210E-\u210F\u2113\u212F\u2134\u2139\u213C-\u213D\u2146-\u2149\u214E\u2184\u2C30-\u2C5E\u2C61\u2C65-\u2C66\u2C68\u2C6A\u2C6C\u2C71\u2C73-\u2C74\u2C76-\u2C7B\u2C81\u2C83\u2C85\u2C87\u2C89\u2C8B\u2C8D\u2C8F\u2C91\u2C93\u2C95\u2C97\u2C99\u2C9B\u2C9D\u2C9F\u2CA1\u2CA3\u2CA5\u2CA7\u2CA9\u2CAB\u2CAD\u2CAF\u2CB1\u2CB3\u2CB5\u2CB7\u2CB9\u2CBB\u2CBD\u2CBF\u2CC1\u2CC3\u2CC5\u2CC7\u2CC9\u2CCB\u2CCD\u2CCF\u2CD1\u2CD3\u2CD5\u2CD7\u2CD9\u2CDB\u2CDD\u2CDF\u2CE1\u2CE3-\u2CE4\u2CEC\u2CEE\u2CF3\u2D00-\u2D25\u2D27\u2D2D\uA641\uA643\uA645\uA647\uA649\uA64B\uA64D\uA64F\uA651\uA653\uA655\uA657\uA659\uA65B\uA65D\uA65F\uA661\uA663\uA665\uA667\uA669\uA66B\uA66D\uA681\uA683\uA685\uA687\uA689\uA68B\uA68D\uA68F\uA691\uA693\uA695\uA697\uA699\uA69B\uA723\uA725\uA727\uA729\uA72B\uA72D\uA72F-\uA731\uA733\uA735\uA737\uA739\uA73B\uA73D\uA73F\uA741\uA743\uA745\uA747\uA749\uA74B\uA74D\uA74F\uA751\uA753\uA755\uA757\uA759\uA75B\uA75D\uA75F\uA761\uA763\uA765\uA767\uA769\uA76B\uA76D\uA76F\uA771-\uA778\uA77A\uA77C\uA77F\uA781\uA783\uA785\uA787\uA78C\uA78E\uA791\uA793-\uA795\uA797\uA799\uA79B\uA79D\uA79F\uA7A1\uA7A3\uA7A5\uA7A7\uA7A9\uA7B5\uA7B7\uA7FA\uAB30-\uAB5A\uAB60-\uAB65\uAB70-\uABBF\uFB00-\uFB06\uFB13-\uFB17\uFF41-\uFF5A] 434 | 435 | // Letter, Modifier 436 | Lm = [\u02B0-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0374\u037A\u0559\u0640\u06E5-\u06E6\u07F4-\u07F5\u07FA\u081A\u0824\u0828\u0971\u0E46\u0EC6\u10FC\u17D7\u1843\u1AA7\u1C78-\u1C7D\u1D2C-\u1D6A\u1D78\u1D9B-\u1DBF\u2071\u207F\u2090-\u209C\u2C7C-\u2C7D\u2D6F\u2E2F\u3005\u3031-\u3035\u303B\u309D-\u309E\u30FC-\u30FE\uA015\uA4F8-\uA4FD\uA60C\uA67F\uA69C-\uA69D\uA717-\uA71F\uA770\uA788\uA7F8-\uA7F9\uA9CF\uA9E6\uAA70\uAADD\uAAF3-\uAAF4\uAB5C-\uAB5F\uFF70\uFF9E-\uFF9F] 437 | 438 | // Letter, Other 439 | Lo = [\u00AA\u00BA\u01BB\u01C0-\u01C3\u0294\u05D0-\u05EA\u05F0-\u05F2\u0620-\u063F\u0641-\u064A\u066E-\u066F\u0671-\u06D3\u06D5\u06EE-\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u0800-\u0815\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0972-\u0980\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0-\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B35-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60-\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0-\u0CE1\u0CF1-\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32-\u0E33\u0E40-\u0E45\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EB0\u0EB2-\u0EB3\u0EBD\u0EC0-\u0EC4\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065-\u1066\u106E-\u1070\u1075-\u1081\u108E\u10D0-\u10FA\u10FD-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17DC\u1820-\u1842\u1844-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE-\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C77\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5-\u1CF6\u2135-\u2138\u2D30-\u2D67\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3006\u303C\u3041-\u3096\u309F\u30A1-\u30FA\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA014\uA016-\uA48C\uA4D0-\uA4F7\uA500-\uA60B\uA610-\uA61F\uA62A-\uA62B\uA66E\uA6A0-\uA6E5\uA78F\uA7F7\uA7FB-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9E0-\uA9E4\uA9E7-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA6F\uAA71-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5-\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADC\uAAE0-\uAAEA\uAAF2\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40-\uFB41\uFB43-\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF66-\uFF6F\uFF71-\uFF9D\uFFA0-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC] 440 | 441 | // Letter, Titlecase 442 | Lt = [\u01C5\u01C8\u01CB\u01F2\u1F88-\u1F8F\u1F98-\u1F9F\u1FA8-\u1FAF\u1FBC\u1FCC\u1FFC] 443 | 444 | // Letter, Uppercase 445 | Lu = [\u0041-\u005A\u00C0-\u00D6\u00D8-\u00DE\u0100\u0102\u0104\u0106\u0108\u010A\u010C\u010E\u0110\u0112\u0114\u0116\u0118\u011A\u011C\u011E\u0120\u0122\u0124\u0126\u0128\u012A\u012C\u012E\u0130\u0132\u0134\u0136\u0139\u013B\u013D\u013F\u0141\u0143\u0145\u0147\u014A\u014C\u014E\u0150\u0152\u0154\u0156\u0158\u015A\u015C\u015E\u0160\u0162\u0164\u0166\u0168\u016A\u016C\u016E\u0170\u0172\u0174\u0176\u0178-\u0179\u017B\u017D\u0181-\u0182\u0184\u0186-\u0187\u0189-\u018B\u018E-\u0191\u0193-\u0194\u0196-\u0198\u019C-\u019D\u019F-\u01A0\u01A2\u01A4\u01A6-\u01A7\u01A9\u01AC\u01AE-\u01AF\u01B1-\u01B3\u01B5\u01B7-\u01B8\u01BC\u01C4\u01C7\u01CA\u01CD\u01CF\u01D1\u01D3\u01D5\u01D7\u01D9\u01DB\u01DE\u01E0\u01E2\u01E4\u01E6\u01E8\u01EA\u01EC\u01EE\u01F1\u01F4\u01F6-\u01F8\u01FA\u01FC\u01FE\u0200\u0202\u0204\u0206\u0208\u020A\u020C\u020E\u0210\u0212\u0214\u0216\u0218\u021A\u021C\u021E\u0220\u0222\u0224\u0226\u0228\u022A\u022C\u022E\u0230\u0232\u023A-\u023B\u023D-\u023E\u0241\u0243-\u0246\u0248\u024A\u024C\u024E\u0370\u0372\u0376\u037F\u0386\u0388-\u038A\u038C\u038E-\u038F\u0391-\u03A1\u03A3-\u03AB\u03CF\u03D2-\u03D4\u03D8\u03DA\u03DC\u03DE\u03E0\u03E2\u03E4\u03E6\u03E8\u03EA\u03EC\u03EE\u03F4\u03F7\u03F9-\u03FA\u03FD-\u042F\u0460\u0462\u0464\u0466\u0468\u046A\u046C\u046E\u0470\u0472\u0474\u0476\u0478\u047A\u047C\u047E\u0480\u048A\u048C\u048E\u0490\u0492\u0494\u0496\u0498\u049A\u049C\u049E\u04A0\u04A2\u04A4\u04A6\u04A8\u04AA\u04AC\u04AE\u04B0\u04B2\u04B4\u04B6\u04B8\u04BA\u04BC\u04BE\u04C0-\u04C1\u04C3\u04C5\u04C7\u04C9\u04CB\u04CD\u04D0\u04D2\u04D4\u04D6\u04D8\u04DA\u04DC\u04DE\u04E0\u04E2\u04E4\u04E6\u04E8\u04EA\u04EC\u04EE\u04F0\u04F2\u04F4\u04F6\u04F8\u04FA\u04FC\u04FE\u0500\u0502\u0504\u0506\u0508\u050A\u050C\u050E\u0510\u0512\u0514\u0516\u0518\u051A\u051C\u051E\u0520\u0522\u0524\u0526\u0528\u052A\u052C\u052E\u0531-\u0556\u10A0-\u10C5\u10C7\u10CD\u13A0-\u13F5\u1E00\u1E02\u1E04\u1E06\u1E08\u1E0A\u1E0C\u1E0E\u1E10\u1E12\u1E14\u1E16\u1E18\u1E1A\u1E1C\u1E1E\u1E20\u1E22\u1E24\u1E26\u1E28\u1E2A\u1E2C\u1E2E\u1E30\u1E32\u1E34\u1E36\u1E38\u1E3A\u1E3C\u1E3E\u1E40\u1E42\u1E44\u1E46\u1E48\u1E4A\u1E4C\u1E4E\u1E50\u1E52\u1E54\u1E56\u1E58\u1E5A\u1E5C\u1E5E\u1E60\u1E62\u1E64\u1E66\u1E68\u1E6A\u1E6C\u1E6E\u1E70\u1E72\u1E74\u1E76\u1E78\u1E7A\u1E7C\u1E7E\u1E80\u1E82\u1E84\u1E86\u1E88\u1E8A\u1E8C\u1E8E\u1E90\u1E92\u1E94\u1E9E\u1EA0\u1EA2\u1EA4\u1EA6\u1EA8\u1EAA\u1EAC\u1EAE\u1EB0\u1EB2\u1EB4\u1EB6\u1EB8\u1EBA\u1EBC\u1EBE\u1EC0\u1EC2\u1EC4\u1EC6\u1EC8\u1ECA\u1ECC\u1ECE\u1ED0\u1ED2\u1ED4\u1ED6\u1ED8\u1EDA\u1EDC\u1EDE\u1EE0\u1EE2\u1EE4\u1EE6\u1EE8\u1EEA\u1EEC\u1EEE\u1EF0\u1EF2\u1EF4\u1EF6\u1EF8\u1EFA\u1EFC\u1EFE\u1F08-\u1F0F\u1F18-\u1F1D\u1F28-\u1F2F\u1F38-\u1F3F\u1F48-\u1F4D\u1F59\u1F5B\u1F5D\u1F5F\u1F68-\u1F6F\u1FB8-\u1FBB\u1FC8-\u1FCB\u1FD8-\u1FDB\u1FE8-\u1FEC\u1FF8-\u1FFB\u2102\u2107\u210B-\u210D\u2110-\u2112\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u2130-\u2133\u213E-\u213F\u2145\u2183\u2C00-\u2C2E\u2C60\u2C62-\u2C64\u2C67\u2C69\u2C6B\u2C6D-\u2C70\u2C72\u2C75\u2C7E-\u2C80\u2C82\u2C84\u2C86\u2C88\u2C8A\u2C8C\u2C8E\u2C90\u2C92\u2C94\u2C96\u2C98\u2C9A\u2C9C\u2C9E\u2CA0\u2CA2\u2CA4\u2CA6\u2CA8\u2CAA\u2CAC\u2CAE\u2CB0\u2CB2\u2CB4\u2CB6\u2CB8\u2CBA\u2CBC\u2CBE\u2CC0\u2CC2\u2CC4\u2CC6\u2CC8\u2CCA\u2CCC\u2CCE\u2CD0\u2CD2\u2CD4\u2CD6\u2CD8\u2CDA\u2CDC\u2CDE\u2CE0\u2CE2\u2CEB\u2CED\u2CF2\uA640\uA642\uA644\uA646\uA648\uA64A\uA64C\uA64E\uA650\uA652\uA654\uA656\uA658\uA65A\uA65C\uA65E\uA660\uA662\uA664\uA666\uA668\uA66A\uA66C\uA680\uA682\uA684\uA686\uA688\uA68A\uA68C\uA68E\uA690\uA692\uA694\uA696\uA698\uA69A\uA722\uA724\uA726\uA728\uA72A\uA72C\uA72E\uA732\uA734\uA736\uA738\uA73A\uA73C\uA73E\uA740\uA742\uA744\uA746\uA748\uA74A\uA74C\uA74E\uA750\uA752\uA754\uA756\uA758\uA75A\uA75C\uA75E\uA760\uA762\uA764\uA766\uA768\uA76A\uA76C\uA76E\uA779\uA77B\uA77D-\uA77E\uA780\uA782\uA784\uA786\uA78B\uA78D\uA790\uA792\uA796\uA798\uA79A\uA79C\uA79E\uA7A0\uA7A2\uA7A4\uA7A6\uA7A8\uA7AA-\uA7AD\uA7B0-\uA7B4\uA7B6\uFF21-\uFF3A] 446 | 447 | // Mark, Spacing Combining 448 | Mc = [\u0903\u093B\u093E-\u0940\u0949-\u094C\u094E-\u094F\u0982-\u0983\u09BE-\u09C0\u09C7-\u09C8\u09CB-\u09CC\u09D7\u0A03\u0A3E-\u0A40\u0A83\u0ABE-\u0AC0\u0AC9\u0ACB-\u0ACC\u0B02-\u0B03\u0B3E\u0B40\u0B47-\u0B48\u0B4B-\u0B4C\u0B57\u0BBE-\u0BBF\u0BC1-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCC\u0BD7\u0C01-\u0C03\u0C41-\u0C44\u0C82-\u0C83\u0CBE\u0CC0-\u0CC4\u0CC7-\u0CC8\u0CCA-\u0CCB\u0CD5-\u0CD6\u0D02-\u0D03\u0D3E-\u0D40\u0D46-\u0D48\u0D4A-\u0D4C\u0D57\u0D82-\u0D83\u0DCF-\u0DD1\u0DD8-\u0DDF\u0DF2-\u0DF3\u0F3E-\u0F3F\u0F7F\u102B-\u102C\u1031\u1038\u103B-\u103C\u1056-\u1057\u1062-\u1064\u1067-\u106D\u1083-\u1084\u1087-\u108C\u108F\u109A-\u109C\u17B6\u17BE-\u17C5\u17C7-\u17C8\u1923-\u1926\u1929-\u192B\u1930-\u1931\u1933-\u1938\u1A19-\u1A1A\u1A55\u1A57\u1A61\u1A63-\u1A64\u1A6D-\u1A72\u1B04\u1B35\u1B3B\u1B3D-\u1B41\u1B43-\u1B44\u1B82\u1BA1\u1BA6-\u1BA7\u1BAA\u1BE7\u1BEA-\u1BEC\u1BEE\u1BF2-\u1BF3\u1C24-\u1C2B\u1C34-\u1C35\u1CE1\u1CF2-\u1CF3\u302E-\u302F\uA823-\uA824\uA827\uA880-\uA881\uA8B4-\uA8C3\uA952-\uA953\uA983\uA9B4-\uA9B5\uA9BA-\uA9BB\uA9BD-\uA9C0\uAA2F-\uAA30\uAA33-\uAA34\uAA4D\uAA7B\uAA7D\uAAEB\uAAEE-\uAAEF\uAAF5\uABE3-\uABE4\uABE6-\uABE7\uABE9-\uABEA\uABEC] 449 | 450 | // Mark, Nonspacing 451 | Mn = [\u0300-\u036F\u0483-\u0487\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08E3-\u0902\u093A\u093C\u0941-\u0948\u094D\u0951-\u0957\u0962-\u0963\u0981\u09BC\u09C1-\u09C4\u09CD\u09E2-\u09E3\u0A01-\u0A02\u0A3C\u0A41-\u0A42\u0A47-\u0A48\u0A4B-\u0A4D\u0A51\u0A70-\u0A71\u0A75\u0A81-\u0A82\u0ABC\u0AC1-\u0AC5\u0AC7-\u0AC8\u0ACD\u0AE2-\u0AE3\u0B01\u0B3C\u0B3F\u0B41-\u0B44\u0B4D\u0B56\u0B62-\u0B63\u0B82\u0BC0\u0BCD\u0C00\u0C3E-\u0C40\u0C46-\u0C48\u0C4A-\u0C4D\u0C55-\u0C56\u0C62-\u0C63\u0C81\u0CBC\u0CBF\u0CC6\u0CCC-\u0CCD\u0CE2-\u0CE3\u0D01\u0D41-\u0D44\u0D4D\u0D62-\u0D63\u0DCA\u0DD2-\u0DD4\u0DD6\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB-\u0EBC\u0EC8-\u0ECD\u0F18-\u0F19\u0F35\u0F37\u0F39\u0F71-\u0F7E\u0F80-\u0F84\u0F86-\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102D-\u1030\u1032-\u1037\u1039-\u103A\u103D-\u103E\u1058-\u1059\u105E-\u1060\u1071-\u1074\u1082\u1085-\u1086\u108D\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752-\u1753\u1772-\u1773\u17B4-\u17B5\u17B7-\u17BD\u17C6\u17C9-\u17D3\u17DD\u180B-\u180D\u18A9\u1920-\u1922\u1927-\u1928\u1932\u1939-\u193B\u1A17-\u1A18\u1A1B\u1A56\u1A58-\u1A5E\u1A60\u1A62\u1A65-\u1A6C\u1A73-\u1A7C\u1A7F\u1AB0-\u1ABD\u1B00-\u1B03\u1B34\u1B36-\u1B3A\u1B3C\u1B42\u1B6B-\u1B73\u1B80-\u1B81\u1BA2-\u1BA5\u1BA8-\u1BA9\u1BAB-\u1BAD\u1BE6\u1BE8-\u1BE9\u1BED\u1BEF-\u1BF1\u1C2C-\u1C33\u1C36-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE0\u1CE2-\u1CE8\u1CED\u1CF4\u1CF8-\u1CF9\u1DC0-\u1DF5\u1DFC-\u1DFF\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302D\u3099-\u309A\uA66F\uA674-\uA67D\uA69E-\uA69F\uA6F0-\uA6F1\uA802\uA806\uA80B\uA825-\uA826\uA8C4\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA951\uA980-\uA982\uA9B3\uA9B6-\uA9B9\uA9BC\uA9E5\uAA29-\uAA2E\uAA31-\uAA32\uAA35-\uAA36\uAA43\uAA4C\uAA7C\uAAB0\uAAB2-\uAAB4\uAAB7-\uAAB8\uAABE-\uAABF\uAAC1\uAAEC-\uAAED\uAAF6\uABE5\uABE8\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F] 452 | 453 | // Number, Decimal Digit 454 | Nd = [\u0030-\u0039\u0660-\u0669\u06F0-\u06F9\u07C0-\u07C9\u0966-\u096F\u09E6-\u09EF\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0BE6-\u0BEF\u0C66-\u0C6F\u0CE6-\u0CEF\u0D66-\u0D6F\u0DE6-\u0DEF\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F29\u1040-\u1049\u1090-\u1099\u17E0-\u17E9\u1810-\u1819\u1946-\u194F\u19D0-\u19D9\u1A80-\u1A89\u1A90-\u1A99\u1B50-\u1B59\u1BB0-\u1BB9\u1C40-\u1C49\u1C50-\u1C59\uA620-\uA629\uA8D0-\uA8D9\uA900-\uA909\uA9D0-\uA9D9\uA9F0-\uA9F9\uAA50-\uAA59\uABF0-\uABF9\uFF10-\uFF19] 455 | 456 | // Number, Letter 457 | Nl = [\u16EE-\u16F0\u2160-\u2182\u2185-\u2188\u3007\u3021-\u3029\u3038-\u303A\uA6E6-\uA6EF] 458 | 459 | // Punctuation, Connector 460 | Pc = [\u005F\u203F-\u2040\u2054\uFE33-\uFE34\uFE4D-\uFE4F\uFF3F] 461 | 462 | // Separator, Space 463 | Zs = [\u0020\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000] 464 | 465 | // Tokens 466 | 467 | BreakToken = "break" !IdentifierPart 468 | CaseToken = "case" !IdentifierPart 469 | CatchToken = "catch" !IdentifierPart 470 | ClassToken = "class" !IdentifierPart 471 | ConstToken = "const" !IdentifierPart 472 | ContinueToken = "continue" !IdentifierPart 473 | DebuggerToken = "debugger" !IdentifierPart 474 | DefaultToken = "default" !IdentifierPart 475 | DeleteToken = "delete" !IdentifierPart 476 | DoToken = "do" !IdentifierPart 477 | ElseToken = "else" !IdentifierPart 478 | EnumToken = "enum" !IdentifierPart 479 | ExportToken = "export" !IdentifierPart 480 | ExtendsToken = "extends" !IdentifierPart 481 | FalseToken = "false" !IdentifierPart 482 | FinallyToken = "finally" !IdentifierPart 483 | ForToken = "for" !IdentifierPart 484 | FunctionToken = "function" !IdentifierPart 485 | GetToken = "get" !IdentifierPart 486 | IfToken = "if" !IdentifierPart 487 | ImportToken = "import" !IdentifierPart 488 | InstanceofToken = "instanceof" !IdentifierPart 489 | InToken = "in" !IdentifierPart 490 | NewToken = "new" !IdentifierPart 491 | NullToken = "null" !IdentifierPart 492 | ReturnToken = "return" !IdentifierPart 493 | SetToken = "set" !IdentifierPart 494 | SuperToken = "super" !IdentifierPart 495 | SwitchToken = "switch" !IdentifierPart 496 | ThisToken = "this" !IdentifierPart 497 | ThrowToken = "throw" !IdentifierPart 498 | TrueToken = "true" !IdentifierPart 499 | TryToken = "try" !IdentifierPart 500 | TypeofToken = "typeof" !IdentifierPart 501 | VarToken = "var" !IdentifierPart 502 | VoidToken = "void" !IdentifierPart 503 | WhileToken = "while" !IdentifierPart 504 | WithToken = "with" !IdentifierPart 505 | 506 | // Skipped 507 | 508 | __ 509 | = (WhiteSpace / LineTerminatorSequence)* 510 | // = (WhiteSpace / LineTerminatorSequence / Comment)* 511 | 512 | _ 513 | = (WhiteSpace)* 514 | // = (WhiteSpace / MultiLineCommentNoLineTerminator)* 515 | 516 | // Automatic Semicolon Insertion 517 | 518 | // EOS 519 | // = __ ";" 520 | // / _ SingleLineComment? LineTerminatorSequence 521 | // / _ &"}" 522 | // / __ EOF 523 | // 524 | // EOF 525 | // = !. 526 | 527 | // // ----- A.2 Number Conversions ----- 528 | // 529 | // // Irrelevant. 530 | // 531 | // // ----- A.3 Expressions ----- 532 | // 533 | //*PrimaryExpression 534 | //* = ThisToken { return { type: "ThisExpression" }; } 535 | //* / Identifier 536 | //* / Literal 537 | //* / ArrayLiteral 538 | //* / ObjectLiteral 539 | //* / "(" __ expression:Expression __ ")" { return expression; } 540 | PrimaryExpression 541 | = Identifier 542 | / Literal 543 | / ArrayLiteral 544 | / ObjectLiteral 545 | / "(" __ expression:VueExpressionNoFilter __ ")" { return expression; } 546 | 547 | ArrayLiteral 548 | = "[" __ elision:(Elision __)? "]" { 549 | return { 550 | type: "ArrayExpression", 551 | elements: optionalList(extractOptional(elision, 0)) 552 | }; 553 | } 554 | / "[" __ elements:ElementList __ "]" { 555 | return { 556 | type: "ArrayExpression", 557 | elements: elements 558 | }; 559 | } 560 | / "[" __ elements:ElementList __ "," __ elision:(Elision __)? "]" { 561 | return { 562 | type: "ArrayExpression", 563 | elements: elements.concat(optionalList(extractOptional(elision, 0))) 564 | }; 565 | } 566 | 567 | //* ElementList 568 | //* = head:( 569 | //* elision:(Elision __)? element:AssignmentExpression { 570 | //* return optionalList(extractOptional(elision, 0)).concat(element); 571 | //* } 572 | //* ) 573 | //* tail:( 574 | //* __ "," __ elision:(Elision __)? element:AssignmentExpression { 575 | //* return optionalList(extractOptional(elision, 0)).concat(element); 576 | //* } 577 | //* )* 578 | //* { return Array.prototype.concat.apply(head, tail); } 579 | 580 | ElementList 581 | = head:( 582 | elision:(Elision __)? element:VueExpressionNoFilter { 583 | return optionalList(extractOptional(elision, 0)).concat(element); 584 | } 585 | ) 586 | tail:( 587 | __ "," __ elision:(Elision __)? element:VueExpressionNoFilter { 588 | return optionalList(extractOptional(elision, 0)).concat(element); 589 | } 590 | )* 591 | { return Array.prototype.concat.apply(head, tail); } 592 | 593 | Elision 594 | = "," commas:(__ ",")* { return filledArray(commas.length + 1, null); } 595 | 596 | ObjectLiteral 597 | = "{" __ "}" { return { type: "ObjectExpression", properties: [] }; } 598 | / "{" __ properties:PropertyNameAndValueList __ "}" { 599 | return { type: "ObjectExpression", properties: properties }; 600 | } 601 | / "{" __ properties:PropertyNameAndValueList __ "," __ "}" { 602 | return { type: "ObjectExpression", properties: properties }; 603 | } 604 | PropertyNameAndValueList 605 | = head:PropertyAssignment tail:(__ "," __ PropertyAssignment)* { 606 | return buildList(head, tail, 3); 607 | } 608 | 609 | PropertyAssignment 610 | //* = key:PropertyName __ ":" __ value:AssignmentExpression { 611 | = key:PropertyName __ ":" __ value:VueExpressionNoFilter { 612 | return { type: "Property", key: key, value: value, kind: "init" }; 613 | } 614 | //* / GetToken __ key:PropertyName __ 615 | //* "(" __ ")" __ 616 | //* "{" __ body:FunctionBody __ "}" 617 | //* { 618 | //* return { 619 | //* type: "Property", 620 | //* key: key, 621 | //* value: { 622 | //* type: "FunctionExpression", 623 | //* id: null, 624 | //* params: [], 625 | //* body: body 626 | //* }, 627 | //* kind: "get" 628 | //* }; 629 | //* } 630 | //* / SetToken __ key:PropertyName __ 631 | //* "(" __ params:PropertySetParameterList __ ")" __ 632 | //* "{" __ body:FunctionBody __ "}" 633 | //* { 634 | //* return { 635 | //* type: "Property", 636 | //* key: key, 637 | //* value: { 638 | //* type: "FunctionExpression", 639 | //* id: null, 640 | //* params: params, 641 | //* body: body 642 | //* }, 643 | //* kind: "set" 644 | //* }; 645 | //* } 646 | 647 | PropertyName 648 | = IdentifierName 649 | / StringLiteral 650 | / NumericLiteral 651 | 652 | // PropertySetParameterList 653 | // = id:Identifier { return [id]; } 654 | // 655 | //*MemberExpression 656 | //* = head:( 657 | //* PrimaryExpression 658 | //* / FunctionExpression 659 | //* / NewToken __ callee:MemberExpression __ args:Arguments { 660 | //* return { type: "NewExpression", callee: callee, arguments: args }; 661 | //* } 662 | //* ) 663 | //* tail:( 664 | //* __ "[" __ property:Expression __ "]" { 665 | //* return { property: property, computed: true }; 666 | //* } 667 | //* / __ "." __ property:IdentifierName { 668 | //* return { property: property, computed: false }; 669 | //* } 670 | //* )* 671 | //* { 672 | //* return tail.reduce(function(result, element) { 673 | //* return { 674 | //* type: "MemberExpression", 675 | //* object: result, 676 | //* property: element.property, 677 | //* computed: element.computed 678 | //* }; 679 | //* }, head); 680 | //* } 681 | 682 | MemberExpression 683 | = head:PrimaryExpression 684 | tail:( 685 | __ "[" __ property:VueExpressionNoFilter __ "]" { 686 | return { property: property, computed: true }; 687 | } 688 | / __ "." __ property:IdentifierName { 689 | return { property: property, computed: false }; 690 | } 691 | )* 692 | { 693 | return tail.reduce(function(result, element) { 694 | return { 695 | type: "MemberExpression", 696 | object: result, 697 | property: element.property, 698 | computed: element.computed 699 | }; 700 | }, head); 701 | } 702 | 703 | // NewExpression 704 | // = MemberExpression 705 | // / NewToken __ callee:NewExpression { 706 | // return { type: "NewExpression", callee: callee, arguments: [] }; 707 | // } 708 | // 709 | // CallExpression 710 | // = head:( 711 | // callee:MemberExpression __ args:Arguments { 712 | // return { type: "CallExpression", callee: callee, arguments: args }; 713 | // } 714 | // ) 715 | // tail:( 716 | // __ args:Arguments { 717 | // return { type: "CallExpression", arguments: args }; 718 | // } 719 | // / __ "[" __ property:Expression __ "]" { 720 | // return { 721 | // type: "MemberExpression", 722 | // property: property, 723 | // computed: true 724 | // }; 725 | // } 726 | // / __ "." __ property:IdentifierName { 727 | // return { 728 | // type: "MemberExpression", 729 | // property: property, 730 | // computed: false 731 | // }; 732 | // } 733 | // )* 734 | // { 735 | // return tail.reduce(function(result, element) { 736 | // element[TYPES_TO_PROPERTY_NAMES[element.type]] = result; 737 | // 738 | // return element; 739 | // }, head); 740 | // } 741 | // 742 | Arguments 743 | = "(" __ args:(ArgumentList __)? ")" { 744 | return optionalList(extractOptional(args, 0)); 745 | } 746 | 747 | //*ArgumentList 748 | //* = head:AssignmentExpression tail:(__ "," __ AssignmentExpression)* { 749 | //* return buildList(head, tail, 3); 750 | //* } 751 | //* 752 | ArgumentList 753 | = head:VueExpressionNoFilter tail:(__ "," __ VueExpressionNoFilter)* { 754 | return buildList(head, tail, 3); 755 | } 756 | 757 | // LeftHandSideExpression 758 | // = CallExpression 759 | // / NewExpression 760 | // 761 | // PostfixExpression 762 | // = argument:LeftHandSideExpression _ operator:PostfixOperator { 763 | // return { 764 | // type: "UpdateExpression", 765 | // operator: operator, 766 | // argument: argument, 767 | // prefix: false 768 | // }; 769 | // } 770 | // / LeftHandSideExpression 771 | // 772 | // PostfixOperator 773 | // = "++" 774 | // / "--" 775 | // 776 | // UnaryExpression 777 | // = PostfixExpression 778 | // / operator:UnaryOperator __ argument:UnaryExpression { 779 | // var type = (operator === "++" || operator === "--") 780 | // ? "UpdateExpression" 781 | // : "UnaryExpression"; 782 | // 783 | // return { 784 | // type: type, 785 | // operator: operator, 786 | // argument: argument, 787 | // prefix: true 788 | // }; 789 | // } 790 | // 791 | // UnaryOperator 792 | // = $DeleteToken 793 | // / $VoidToken 794 | // / $TypeofToken 795 | // / "++" 796 | // / "--" 797 | // / $("+" !"=") 798 | // / $("-" !"=") 799 | // / "~" 800 | // / "!" 801 | // 802 | //*MultiplicativeExpression 803 | //* = head:UnaryExpression 804 | //* tail:(__ MultiplicativeOperator __ UnaryExpression)* 805 | //* { return buildBinaryExpression(head, tail); } 806 | 807 | MultiplicativeExpression 808 | = head:MemberExpression 809 | tail:(__ MultiplicativeOperator __ MemberExpression)* 810 | { return buildBinaryExpression(head, tail); } 811 | 812 | MultiplicativeOperator 813 | = $("*" !"=") 814 | / $("/" !"=") 815 | / $("%" !"=") 816 | 817 | AdditiveExpression 818 | = head:MultiplicativeExpression 819 | tail:(__ AdditiveOperator __ MultiplicativeExpression)* 820 | { return buildBinaryExpression(head, tail); } 821 | 822 | AdditiveOperator 823 | = $("+" ![+=]) 824 | / $("-" ![-=]) 825 | 826 | // ShiftExpression 827 | // = head:AdditiveExpression 828 | // tail:(__ ShiftOperator __ AdditiveExpression)* 829 | // { return buildBinaryExpression(head, tail); } 830 | // 831 | // ShiftOperator 832 | // = $("<<" !"=") 833 | // / $(">>>" !"=") 834 | // / $(">>" !"=") 835 | // 836 | // RelationalExpression 837 | // = head:ShiftExpression 838 | // tail:(__ RelationalOperator __ ShiftExpression)* 839 | // { return buildBinaryExpression(head, tail); } 840 | // 841 | // RelationalOperator 842 | // = "<=" 843 | // / ">=" 844 | // / $("<" !"<") 845 | // / $(">" !">") 846 | // / $InstanceofToken 847 | // / $InToken 848 | // 849 | //*RelationalExpressionNoIn 850 | //* = head:ShiftExpression 851 | //* tail:(__ RelationalOperatorNoIn __ ShiftExpression)* 852 | //* { return buildBinaryExpression(head, tail); } 853 | RelationalExpressionNoIn 854 | = head:AdditiveExpression 855 | tail:(__ RelationalOperatorNoIn __ AdditiveExpression)* 856 | { return buildBinaryExpression(head, tail); } 857 | 858 | RelationalOperatorNoIn 859 | = "<=" 860 | / ">=" 861 | / $("<" !"<") 862 | / $(">" !">") 863 | //* / $InstanceofToken 864 | 865 | // EqualityExpression 866 | // = head:RelationalExpression 867 | // tail:(__ EqualityOperator __ RelationalExpression)* 868 | // { return buildBinaryExpression(head, tail); } 869 | // 870 | EqualityExpressionNoIn 871 | = head:RelationalExpressionNoIn 872 | tail:(__ EqualityOperator __ RelationalExpressionNoIn)* 873 | { return buildBinaryExpression(head, tail); } 874 | 875 | EqualityOperator 876 | = "===" 877 | / "!==" 878 | / "==" 879 | / "!=" 880 | 881 | // BitwiseANDExpression 882 | // = head:EqualityExpression 883 | // tail:(__ BitwiseANDOperator __ EqualityExpression)* 884 | // { return buildBinaryExpression(head, tail); } 885 | // 886 | // BitwiseANDExpressionNoIn 887 | // = head:EqualityExpressionNoIn 888 | // tail:(__ BitwiseANDOperator __ EqualityExpressionNoIn)* 889 | // { return buildBinaryExpression(head, tail); } 890 | // 891 | // BitwiseANDOperator 892 | // = $("&" ![&=]) 893 | // 894 | // BitwiseXORExpression 895 | // = head:BitwiseANDExpression 896 | // tail:(__ BitwiseXOROperator __ BitwiseANDExpression)* 897 | // { return buildBinaryExpression(head, tail); } 898 | // 899 | // BitwiseXORExpressionNoIn 900 | // = head:BitwiseANDExpressionNoIn 901 | // tail:(__ BitwiseXOROperator __ BitwiseANDExpressionNoIn)* 902 | // { return buildBinaryExpression(head, tail); } 903 | // 904 | // BitwiseXOROperator 905 | // = $("^" !"=") 906 | // 907 | // BitwiseORExpression 908 | // = head:BitwiseXORExpression 909 | // tail:(__ BitwiseOROperator __ BitwiseXORExpression)* 910 | // { return buildBinaryExpression(head, tail); } 911 | // 912 | // BitwiseORExpressionNoIn 913 | // = head:BitwiseXORExpressionNoIn 914 | // tail:(__ BitwiseOROperator __ BitwiseXORExpressionNoIn)* 915 | // { return buildBinaryExpression(head, tail); } 916 | // 917 | // BitwiseOROperator 918 | // = $("|" ![|=]) 919 | // 920 | // LogicalANDExpression 921 | // = head:BitwiseORExpression 922 | // tail:(__ LogicalANDOperator __ BitwiseORExpression)* 923 | // { return buildLogicalExpression(head, tail); } 924 | // 925 | //*LogicalANDExpressionNoIn 926 | //* = head:BitwiseORExpressionNoIn 927 | //* tail:(__ LogicalANDOperator __ BitwiseORExpressionNoIn)* 928 | //* { return buildLogicalExpression(head, tail); } 929 | 930 | LogicalANDExpressionNoIn 931 | = head:EqualityExpressionNoIn 932 | tail:(__ LogicalANDOperator __ EqualityExpressionNoIn)* 933 | { return buildLogicalExpression(head, tail); } 934 | 935 | 936 | LogicalANDOperator 937 | = "&&" 938 | 939 | // LogicalORExpression 940 | // = head:LogicalANDExpression 941 | // tail:(__ LogicalOROperator __ LogicalANDExpression)* 942 | // { return buildLogicalExpression(head, tail); } 943 | // 944 | LogicalORExpressionNoIn 945 | = head:LogicalANDExpressionNoIn 946 | tail:(__ LogicalOROperator __ LogicalANDExpressionNoIn)* 947 | { return buildLogicalExpression(head, tail); } 948 | 949 | LogicalOROperator 950 | = "||" 951 | 952 | // ConditionalExpression 953 | // = test:LogicalORExpression __ 954 | // "?" __ consequent:AssignmentExpression __ 955 | // ":" __ alternate:AssignmentExpression 956 | // { 957 | // return { 958 | // type: "ConditionalExpression", 959 | // test: test, 960 | // consequent: consequent, 961 | // alternate: alternate 962 | // }; 963 | // } 964 | // / LogicalORExpression 965 | // 966 | //*ConditionalExpressionNoIn 967 | //* = test:LogicalORExpressionNoIn __ 968 | //* "?" __ consequent:AssignmentExpression __ 969 | //* ":" __ alternate:AssignmentExpressionNoIn 970 | //* { 971 | //* return { 972 | //* type: "ConditionalExpression", 973 | //* test: test, 974 | //* consequent: consequent, 975 | //* alternate: alternate 976 | //* }; 977 | //* } 978 | //* / LogicalORExpressionNoIn 979 | // 980 | ConditionalExpressionNoIn 981 | = test:LogicalORExpressionNoIn __ 982 | "?" __ consequent:VueExpressionNoFilter __ 983 | ":" __ alternate:VueExpressionNoFilter 984 | { 985 | return { 986 | type: "ConditionalExpression", 987 | test: test, 988 | consequent: consequent, 989 | alternate: alternate 990 | }; 991 | } 992 | / LogicalORExpressionNoIn 993 | // 994 | // AssignmentExpression 995 | // = left:LeftHandSideExpression __ 996 | // "=" !"=" __ 997 | // right:AssignmentExpression 998 | // { 999 | // return { 1000 | // type: "AssignmentExpression", 1001 | // operator: "=", 1002 | // left: left, 1003 | // right: right 1004 | // }; 1005 | // } 1006 | // / left:LeftHandSideExpression __ 1007 | // operator:AssignmentOperator __ 1008 | // right:AssignmentExpression 1009 | // { 1010 | // return { 1011 | // type: "AssignmentExpression", 1012 | // operator: operator, 1013 | // left: left, 1014 | // right: right 1015 | // }; 1016 | // } 1017 | // / ConditionalExpression 1018 | // 1019 | // AssignmentExpressionNoIn 1020 | // = left:LeftHandSideExpression __ 1021 | // "=" !"=" __ 1022 | // right:AssignmentExpressionNoIn 1023 | // { 1024 | // return { 1025 | // type: "AssignmentExpression", 1026 | // operator: "=", 1027 | // left: left, 1028 | // right: right 1029 | // }; 1030 | // } 1031 | // / left:LeftHandSideExpression __ 1032 | // operator:AssignmentOperator __ 1033 | // right:AssignmentExpressionNoIn 1034 | // { 1035 | // return { 1036 | // type: "AssignmentExpression", 1037 | // operator: operator, 1038 | // left: left, 1039 | // right: right 1040 | // }; 1041 | // } 1042 | // / ConditionalExpressionNoIn 1043 | // 1044 | // AssignmentOperator 1045 | // = "*=" 1046 | // / "/=" 1047 | // / "%=" 1048 | // / "+=" 1049 | // / "-=" 1050 | // / "<<=" 1051 | // / ">>=" 1052 | // / ">>>=" 1053 | // / "&=" 1054 | // / "^=" 1055 | // / "|=" 1056 | // 1057 | // Expression 1058 | // = head:AssignmentExpression tail:(__ "," __ AssignmentExpression)* { 1059 | // return tail.length > 0 1060 | // ? { type: "SequenceExpression", expressions: buildList(head, tail, 3) } 1061 | // : head; 1062 | // } 1063 | // 1064 | // ExpressionNoIn 1065 | // = head:AssignmentExpressionNoIn tail:(__ "," __ AssignmentExpressionNoIn)* { 1066 | // return tail.length > 0 1067 | // ? { type: "SequenceExpression", expressions: buildList(head, tail, 3) } 1068 | // : head; 1069 | // } 1070 | // 1071 | // // ----- A.4 Statements ----- 1072 | // 1073 | // Statement 1074 | // = Block 1075 | // / VariableStatement 1076 | // / EmptyStatement 1077 | // / ExpressionStatement 1078 | // / IfStatement 1079 | // / IterationStatement 1080 | // / ContinueStatement 1081 | // / BreakStatement 1082 | // / ReturnStatement 1083 | // / WithStatement 1084 | // / LabelledStatement 1085 | // / SwitchStatement 1086 | // / ThrowStatement 1087 | // / TryStatement 1088 | // / DebuggerStatement 1089 | // 1090 | // Block 1091 | // = "{" __ body:(StatementList __)? "}" { 1092 | // return { 1093 | // type: "BlockStatement", 1094 | // body: optionalList(extractOptional(body, 0)) 1095 | // }; 1096 | // } 1097 | // 1098 | // StatementList 1099 | // = head:Statement tail:(__ Statement)* { return buildList(head, tail, 1); } 1100 | // 1101 | // VariableStatement 1102 | // = VarToken __ declarations:VariableDeclarationList EOS { 1103 | // return { 1104 | // type: "VariableDeclaration", 1105 | // declarations: declarations, 1106 | // kind: "var" 1107 | // }; 1108 | // } 1109 | // 1110 | // VariableDeclarationList 1111 | // = head:VariableDeclaration tail:(__ "," __ VariableDeclaration)* { 1112 | // return buildList(head, tail, 3); 1113 | // } 1114 | // 1115 | // VariableDeclarationListNoIn 1116 | // = head:VariableDeclarationNoIn tail:(__ "," __ VariableDeclarationNoIn)* { 1117 | // return buildList(head, tail, 3); 1118 | // } 1119 | // 1120 | // VariableDeclaration 1121 | // = id:Identifier init:(__ Initialiser)? { 1122 | // return { 1123 | // type: "VariableDeclarator", 1124 | // id: id, 1125 | // init: extractOptional(init, 1) 1126 | // }; 1127 | // } 1128 | // 1129 | // VariableDeclarationNoIn 1130 | // = id:Identifier init:(__ InitialiserNoIn)? { 1131 | // return { 1132 | // type: "VariableDeclarator", 1133 | // id: id, 1134 | // init: extractOptional(init, 1) 1135 | // }; 1136 | // } 1137 | // 1138 | // Initialiser 1139 | // = "=" !"=" __ expression:AssignmentExpression { return expression; } 1140 | // 1141 | // InitialiserNoIn 1142 | // = "=" !"=" __ expression:AssignmentExpressionNoIn { return expression; } 1143 | // 1144 | // EmptyStatement 1145 | // = ";" { return { type: "EmptyStatement" }; } 1146 | // 1147 | // ExpressionStatement 1148 | // = !("{" / FunctionToken) expression:Expression EOS { 1149 | // return { 1150 | // type: "ExpressionStatement", 1151 | // expression: expression 1152 | // }; 1153 | // } 1154 | // 1155 | // IfStatement 1156 | // = IfToken __ "(" __ test:Expression __ ")" __ 1157 | // consequent:Statement __ 1158 | // ElseToken __ 1159 | // alternate:Statement 1160 | // { 1161 | // return { 1162 | // type: "IfStatement", 1163 | // test: test, 1164 | // consequent: consequent, 1165 | // alternate: alternate 1166 | // }; 1167 | // } 1168 | // / IfToken __ "(" __ test:Expression __ ")" __ 1169 | // consequent:Statement { 1170 | // return { 1171 | // type: "IfStatement", 1172 | // test: test, 1173 | // consequent: consequent, 1174 | // alternate: null 1175 | // }; 1176 | // } 1177 | // 1178 | // IterationStatement 1179 | // = DoToken __ 1180 | // body:Statement __ 1181 | // WhileToken __ "(" __ test:Expression __ ")" EOS 1182 | // { return { type: "DoWhileStatement", body: body, test: test }; } 1183 | // / WhileToken __ "(" __ test:Expression __ ")" __ 1184 | // body:Statement 1185 | // { return { type: "WhileStatement", test: test, body: body }; } 1186 | // / ForToken __ 1187 | // "(" __ 1188 | // init:(ExpressionNoIn __)? ";" __ 1189 | // test:(Expression __)? ";" __ 1190 | // update:(Expression __)? 1191 | // ")" __ 1192 | // body:Statement 1193 | // { 1194 | // return { 1195 | // type: "ForStatement", 1196 | // init: extractOptional(init, 0), 1197 | // test: extractOptional(test, 0), 1198 | // update: extractOptional(update, 0), 1199 | // body: body 1200 | // }; 1201 | // } 1202 | // / ForToken __ 1203 | // "(" __ 1204 | // VarToken __ declarations:VariableDeclarationListNoIn __ ";" __ 1205 | // test:(Expression __)? ";" __ 1206 | // update:(Expression __)? 1207 | // ")" __ 1208 | // body:Statement 1209 | // { 1210 | // return { 1211 | // type: "ForStatement", 1212 | // init: { 1213 | // type: "VariableDeclaration", 1214 | // declarations: declarations, 1215 | // kind: "var" 1216 | // }, 1217 | // test: extractOptional(test, 0), 1218 | // update: extractOptional(update, 0), 1219 | // body: body 1220 | // }; 1221 | // } 1222 | // / ForToken __ 1223 | // "(" __ 1224 | // left:LeftHandSideExpression __ 1225 | // InToken __ 1226 | // right:Expression __ 1227 | // ")" __ 1228 | // body:Statement 1229 | // { 1230 | // return { 1231 | // type: "ForInStatement", 1232 | // left: left, 1233 | // right: right, 1234 | // body: body 1235 | // }; 1236 | // } 1237 | // / ForToken __ 1238 | // "(" __ 1239 | // VarToken __ declarations:VariableDeclarationListNoIn __ 1240 | // InToken __ 1241 | // right:Expression __ 1242 | // ")" __ 1243 | // body:Statement 1244 | // { 1245 | // return { 1246 | // type: "ForInStatement", 1247 | // left: { 1248 | // type: "VariableDeclaration", 1249 | // declarations: declarations, 1250 | // kind: "var" 1251 | // }, 1252 | // right: right, 1253 | // body: body 1254 | // }; 1255 | // } 1256 | // 1257 | // ContinueStatement 1258 | // = ContinueToken EOS { 1259 | // return { type: "ContinueStatement", label: null }; 1260 | // } 1261 | // / ContinueToken _ label:Identifier EOS { 1262 | // return { type: "ContinueStatement", label: label }; 1263 | // } 1264 | // 1265 | // BreakStatement 1266 | // = BreakToken EOS { 1267 | // return { type: "BreakStatement", label: null }; 1268 | // } 1269 | // / BreakToken _ label:Identifier EOS { 1270 | // return { type: "BreakStatement", label: label }; 1271 | // } 1272 | // 1273 | // ReturnStatement 1274 | // = ReturnToken EOS { 1275 | // return { type: "ReturnStatement", argument: null }; 1276 | // } 1277 | // / ReturnToken _ argument:Expression EOS { 1278 | // return { type: "ReturnStatement", argument: argument }; 1279 | // } 1280 | // 1281 | // WithStatement 1282 | // = WithToken __ "(" __ object:Expression __ ")" __ 1283 | // body:Statement 1284 | // { return { type: "WithStatement", object: object, body: body }; } 1285 | // 1286 | // SwitchStatement 1287 | // = SwitchToken __ "(" __ discriminant:Expression __ ")" __ 1288 | // cases:CaseBlock 1289 | // { 1290 | // return { 1291 | // type: "SwitchStatement", 1292 | // discriminant: discriminant, 1293 | // cases: cases 1294 | // }; 1295 | // } 1296 | // 1297 | // CaseBlock 1298 | // = "{" __ clauses:(CaseClauses __)? "}" { 1299 | // return optionalList(extractOptional(clauses, 0)); 1300 | // } 1301 | // / "{" __ 1302 | // before:(CaseClauses __)? 1303 | // default_:DefaultClause __ 1304 | // after:(CaseClauses __)? "}" 1305 | // { 1306 | // return optionalList(extractOptional(before, 0)) 1307 | // .concat(default_) 1308 | // .concat(optionalList(extractOptional(after, 0))); 1309 | // } 1310 | // 1311 | // CaseClauses 1312 | // = head:CaseClause tail:(__ CaseClause)* { return buildList(head, tail, 1); } 1313 | // 1314 | // CaseClause 1315 | // = CaseToken __ test:Expression __ ":" consequent:(__ StatementList)? { 1316 | // return { 1317 | // type: "SwitchCase", 1318 | // test: test, 1319 | // consequent: optionalList(extractOptional(consequent, 1)) 1320 | // }; 1321 | // } 1322 | // 1323 | // DefaultClause 1324 | // = DefaultToken __ ":" consequent:(__ StatementList)? { 1325 | // return { 1326 | // type: "SwitchCase", 1327 | // test: null, 1328 | // consequent: optionalList(extractOptional(consequent, 1)) 1329 | // }; 1330 | // } 1331 | // 1332 | // LabelledStatement 1333 | // = label:Identifier __ ":" __ body:Statement { 1334 | // return { type: "LabeledStatement", label: label, body: body }; 1335 | // } 1336 | // 1337 | // ThrowStatement 1338 | // = ThrowToken _ argument:Expression EOS { 1339 | // return { type: "ThrowStatement", argument: argument }; 1340 | // } 1341 | // 1342 | // TryStatement 1343 | // = TryToken __ block:Block __ handler:Catch __ finalizer:Finally { 1344 | // return { 1345 | // type: "TryStatement", 1346 | // block: block, 1347 | // handler: handler, 1348 | // finalizer: finalizer 1349 | // }; 1350 | // } 1351 | // / TryToken __ block:Block __ handler:Catch { 1352 | // return { 1353 | // type: "TryStatement", 1354 | // block: block, 1355 | // handler: handler, 1356 | // finalizer: null 1357 | // }; 1358 | // } 1359 | // / TryToken __ block:Block __ finalizer:Finally { 1360 | // return { 1361 | // type: "TryStatement", 1362 | // block: block, 1363 | // handler: null, 1364 | // finalizer: finalizer 1365 | // }; 1366 | // } 1367 | // 1368 | // Catch 1369 | // = CatchToken __ "(" __ param:Identifier __ ")" __ body:Block { 1370 | // return { 1371 | // type: "CatchClause", 1372 | // param: param, 1373 | // body: body 1374 | // }; 1375 | // } 1376 | // 1377 | // Finally 1378 | // = FinallyToken __ block:Block { return block; } 1379 | // 1380 | // DebuggerStatement 1381 | // = DebuggerToken EOS { return { type: "DebuggerStatement" }; } 1382 | // 1383 | // // ----- A.5 Functions and Programs ----- 1384 | // 1385 | // FunctionDeclaration 1386 | // = FunctionToken __ id:Identifier __ 1387 | // "(" __ params:(FormalParameterList __)? ")" __ 1388 | // "{" __ body:FunctionBody __ "}" 1389 | // { 1390 | // return { 1391 | // type: "FunctionDeclaration", 1392 | // id: id, 1393 | // params: optionalList(extractOptional(params, 0)), 1394 | // body: body 1395 | // }; 1396 | // } 1397 | // 1398 | // FunctionExpression 1399 | // = FunctionToken __ id:(Identifier __)? 1400 | // "(" __ params:(FormalParameterList __)? ")" __ 1401 | // "{" __ body:FunctionBody __ "}" 1402 | // { 1403 | // return { 1404 | // type: "FunctionExpression", 1405 | // id: extractOptional(id, 0), 1406 | // params: optionalList(extractOptional(params, 0)), 1407 | // body: body 1408 | // }; 1409 | // } 1410 | // 1411 | // FormalParameterList 1412 | // = head:Identifier tail:(__ "," __ Identifier)* { 1413 | // return buildList(head, tail, 3); 1414 | // } 1415 | // 1416 | // FunctionBody 1417 | // = body:SourceElements? { 1418 | // return { 1419 | // type: "BlockStatement", 1420 | // body: optionalList(body) 1421 | // }; 1422 | // } 1423 | // 1424 | // Program 1425 | // = body:SourceElements? { 1426 | // return { 1427 | // type: "Program", 1428 | // body: optionalList(body) 1429 | // }; 1430 | // } 1431 | // 1432 | // SourceElements 1433 | // = head:SourceElement tail:(__ SourceElement)* { 1434 | // return buildList(head, tail, 1); 1435 | // } 1436 | // 1437 | // SourceElement 1438 | // = Statement 1439 | // / FunctionDeclaration 1440 | // 1441 | // // ----- A.6 Universal Resource Identifier Character Classes ----- 1442 | // 1443 | // // Irrelevant. 1444 | // 1445 | // // ----- A.7 Regular Expressions ----- 1446 | // 1447 | // // Irrelevant. 1448 | // 1449 | // // ----- A.8 JSON ----- 1450 | // 1451 | // // Irrelevant. 1452 | --------------------------------------------------------------------------------