Best JavaScript code snippet using playwright-internal
metalJSXComponent-1.0.4.js
Source:metalJSXComponent-1.0.4.js
...3151 * @return {boolean}3152 * @protected3153 */3154 IncrementalDomRenderer.prototype.handleChildRender_ = function handleChildRender_(node) {3155 if (node.tag && _IncrementalDomUtils2.default.isComponentTag(node.tag)) {3156 node.config.children = this.buildChildren_(node.config.children);3157 this.renderFromTag_(node.tag, node.config);3158 return true;3159 }3160 };3161 /**3162 * Handles the `stateKeyChanged` event. Overrides original method from3163 * `ComponentRenderer` to guarantee that `IncrementalDomRenderer`'s logic3164 * will run first.3165 * @param {!Object} data3166 * @override3167 * @protected3168 */3169 IncrementalDomRenderer.prototype.handleComponentRendererStateKeyChanged_ = function handleComponentRendererStateKeyChanged_(data) {3170 this.handleStateKeyChanged_(data);3171 _ComponentRenderer.prototype.handleComponentRendererStateKeyChanged_.call(this, data);3172 };3173 /**3174 * Handles an intercepted call to the `elementOpen` function from incremental3175 * dom.3176 * @param {!function()} originalFn The original function before interception.3177 * @param {string} tag3178 * @protected3179 */3180 IncrementalDomRenderer.prototype.handleInterceptedOpenCall_ = function handleInterceptedOpenCall_(originalFn, tag) {3181 if (_IncrementalDomUtils2.default.isComponentTag(tag)) {3182 return this.handleSubComponentCall_.apply(this, arguments);3183 } else {3184 return this.handleRegularCall_.apply(this, arguments);3185 }3186 };3187 /**3188 * Handles an intercepted call to the `elementOpen` function from incremental3189 * dom, done for a regular element. Adds any inline listeners found and makes3190 * sure that component root elements are always reused.3191 * @param {!function()} originalFn The original function before interception.3192 * @protected3193 */3194 IncrementalDomRenderer.prototype.handleRegularCall_ = function handleRegularCall_(originalFn) {3195 var currComp = IncrementalDomRenderer.getComponentBeingRendered();3196 var currRenderer = currComp.getRenderer();3197 for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {3198 args[_key - 1] = arguments[_key];3199 }3200 if (!currRenderer.rootElementReached_ && currComp.config.key) {3201 args[1] = currComp.config.key;3202 }3203 var node = originalFn.apply(null, args);3204 this.attachDecoratedListeners_(node, args);3205 this.updateElementIfNotReached_(node);3206 return node;3207 };3208 /**3209 * Handles the `stateKeyChanged` event. Stores state properties that have3210 * changed since the last render.3211 * @param {!Object} data3212 * @protected3213 */3214 IncrementalDomRenderer.prototype.handleStateKeyChanged_ = function handleStateKeyChanged_(data) {3215 this.changes_[data.key] = data;3216 };3217 /**3218 * Handles an intercepted call to the `elementOpen` function from incremental3219 * dom, done for a sub component element. Creates and updates the appropriate3220 * sub component.3221 * @param {!function()} originalFn The original function before interception.3222 * @protected3223 */3224 IncrementalDomRenderer.prototype.handleSubComponentCall_ = function handleSubComponentCall_(originalFn) {3225 for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {3226 args[_key2 - 1] = arguments[_key2];3227 }3228 var config = _IncrementalDomUtils2.default.buildConfigFromCall(args);3229 config.ref = _metal.core.isDefAndNotNull(config.ref) ? config.ref : this.buildRef(args[0]);3230 this.componentToRender_ = {3231 config: config,3232 tag: args[0]3233 };3234 this.prevPrefix_ = this.currentPrefix_;3235 this.currentPrefix_ = config.ref;3236 this.generatedRefCount_[this.currentPrefix_] = 0;3237 _IncrementalDomChildren2.default.capture(this, this.handleChildrenCaptured_);3238 };3239 /**3240 * Intercepts incremental dom calls from this component.3241 * @protected3242 */3243 IncrementalDomRenderer.prototype.intercept_ = function intercept_() {3244 _IncrementalDomAop2.default.startInterception({3245 attributes: this.handleInterceptedAttributesCall_,3246 elementOpen: this.handleInterceptedOpenCall_3247 });3248 };3249 /**3250 * Checks if the given object is an incremental dom node.3251 * @param {!Object} node3252 * @return {boolean}3253 */3254 IncrementalDomRenderer.isIncDomNode = function isIncDomNode(node) {3255 return !!node[_IncrementalDomChildren2.default.CHILD_OWNER];3256 };3257 /**3258 * Returns the event name if the given attribute is a listener (of the form3259 * "on<EventName>"), or null if it isn't.3260 * @param {string} attr3261 * @return {?string}3262 * @protected3263 */3264 IncrementalDomRenderer.prototype.getEventFromListenerAttr_ = function getEventFromListenerAttr_(attr) {3265 var matches = IncrementalDomRenderer.LISTENER_REGEX.exec(attr);3266 var eventName = matches ? matches[1] ? matches[1] : matches[2] : null;3267 return eventName ? eventName.toLowerCase() : null;3268 };3269 /**3270 * Gets the component that is this component's parent (that is, the one that3271 * actually rendered it), or null if there's no parent.3272 * @return {Component}3273 */3274 IncrementalDomRenderer.prototype.getParent = function getParent() {3275 return this.parent_;3276 };3277 /**3278 * Gets the component that is this component's owner (that is, the one that3279 * passed its config properties and holds its ref), or null if there's none.3280 * @return {Component}3281 */3282 IncrementalDomRenderer.prototype.getOwner = function getOwner() {3283 return this.owner_;3284 };3285 /**3286 * Creates and renders the given function, which can either be a simple3287 * incremental dom function or a component constructor.3288 * @param {!function()} fnOrCtor Either be a simple incremental dom function3289 or a component constructor.3290 * @param {Object|Element=} opt_dataOrElement Optional config data for the3291 * function or parent for the rendered content.3292 * @param {Element=} opt_element Optional parent for the rendered content.3293 * @return {!Component} The rendered component's instance.3294 */3295 IncrementalDomRenderer.render = function render(fnOrCtor, opt_dataOrElement, opt_parent) {3296 if (!_metalComponent.Component.isComponentCtor(fnOrCtor)) {3297 var fn = fnOrCtor;3298 var TempComponent = function (_Component) {3299 _inherits(TempComponent, _Component);3300 function TempComponent() {3301 _classCallCheck(this, TempComponent);3302 return _possibleConstructorReturn(this, _Component.apply(this, arguments));3303 }3304 TempComponent.prototype.created = function created() {3305 if (IncrementalDomRenderer.getComponentBeingRendered()) {3306 this.getRenderer().updateContext_(this);3307 }3308 };3309 TempComponent.prototype.render = function render() {3310 fn(this.config);3311 };3312 return TempComponent;3313 }(_metalComponent.Component);3314 TempComponent.RENDERER = IncrementalDomRenderer;3315 fnOrCtor = TempComponent;3316 }3317 return _metalComponent.Component.render(fnOrCtor, opt_dataOrElement, opt_parent);3318 };3319 /**3320 * Renders the renderer's component for the first time, patching its element3321 * through the incremental dom function calls done by `renderIncDom`.3322 */3323 IncrementalDomRenderer.prototype.render = function render() {3324 this.patch();3325 };3326 /**3327 * Renders the given child node via its owner renderer.3328 * @param {!Object} child3329 */3330 IncrementalDomRenderer.renderChild = function renderChild(child) {3331 child[_IncrementalDomChildren2.default.CHILD_OWNER].renderChild(child);3332 };3333 /**3334 * Renders the given child node.3335 * @param {!Object} child3336 */3337 IncrementalDomRenderer.prototype.renderChild = function renderChild(child) {3338 this.intercept_();3339 _IncrementalDomChildren2.default.render(child, this.handleChildRender_);3340 _IncrementalDomAop2.default.stopInterception();3341 };3342 /**3343 * Renders the contents for the given tag.3344 * @param {!function()|string} tag3345 * @param {!Object} config3346 * @protected3347 */3348 IncrementalDomRenderer.prototype.renderFromTag_ = function renderFromTag_(tag, config) {3349 if (_metal.core.isString(tag) || tag.prototype.getRenderer) {3350 var comp = this.renderSubComponent_(tag, config);3351 this.updateElementIfNotReached_(comp.element);3352 return comp.element;3353 } else {3354 return tag(config);3355 }3356 };3357 /**3358 * Calls functions from `IncrementalDOM` to build the component element's3359 * content. Can be overriden by subclasses (for integration with template3360 * engines for example).3361 */3362 IncrementalDomRenderer.prototype.renderIncDom = function renderIncDom() {3363 if (this.component_.render) {3364 this.component_.render();3365 } else {3366 IncrementalDOM.elementVoid('div');3367 }3368 };3369 /**3370 * Runs the incremental dom functions for rendering this component, but3371 * doesn't call `patch` yet. Rather, this will be the function that should be3372 * called by `patch`.3373 */3374 IncrementalDomRenderer.prototype.renderInsidePatch = function renderInsidePatch() {3375 if (this.component_.wasRendered && !this.shouldUpdate(this.changes_) && IncrementalDOM.currentPointer() === this.component_.element) {3376 if (this.component_.element) {3377 IncrementalDOM.skipNode();3378 }3379 return;3380 }3381 this.renderInsidePatchDontSkip_();3382 };3383 /**3384 * The same as `renderInsidePatch`, but without the check that may skip the3385 * render action.3386 * @protected3387 */3388 IncrementalDomRenderer.prototype.renderInsidePatchDontSkip_ = function renderInsidePatchDontSkip_() {3389 IncrementalDomRenderer.startedRenderingComponent(this.component_);3390 this.changes_ = {};3391 this.rootElementReached_ = false;3392 _IncrementalDomUnusedComponents2.default.schedule(this.childComponents_ || []);3393 this.childComponents_ = [];3394 this.generatedRefCount_ = {};3395 this.listenersToAttach_ = [];3396 this.currentPrefix_ = '';3397 this.intercept_();3398 this.renderIncDom();3399 _IncrementalDomAop2.default.stopInterception();3400 if (!this.rootElementReached_) {3401 this.component_.element = null;3402 } else {3403 this.component_.addElementClasses();3404 }3405 this.emit('rendered', !this.isRendered_);3406 IncrementalDomRenderer.finishedRenderingComponent();3407 };3408 /**3409 * This updates the sub component that is represented by the given data.3410 * The sub component is created, added to its parent and rendered. If it3411 * had already been rendered before though, it will only have its state3412 * updated instead.3413 * @param {string|!function()} tagOrCtor The tag name or constructor function.3414 * @param {!Object} config The config object for the sub component.3415 * @return {!Component} The updated sub component.3416 * @protected3417 */3418 IncrementalDomRenderer.prototype.renderSubComponent_ = function renderSubComponent_(tagOrCtor, config) {3419 var comp = this.getSubComponent_(tagOrCtor, config);3420 this.updateContext_(comp);3421 var renderer = comp.getRenderer();3422 if (renderer instanceof IncrementalDomRenderer) {3423 var parentComp = IncrementalDomRenderer.getComponentBeingRendered();3424 parentComp.getRenderer().childComponents_.push(comp);3425 renderer.parent_ = parentComp;3426 renderer.owner_ = this.component_;3427 renderer.renderInsidePatch();3428 } else {3429 console.warn('IncrementalDomRenderer doesn\'t support rendering sub components ' + 'that don\'t use IncrementalDomRenderer as well, like:', comp);3430 }3431 if (!comp.wasRendered) {3432 comp.renderAsSubComponent();3433 }3434 return comp;3435 };3436 /**3437 * Sets the component's config object with its new value.3438 * @param {!Component} comp The component to set the config for.3439 * @param {!Object} config3440 * @protected3441 */3442 IncrementalDomRenderer.prototype.setConfig_ = function setConfig_(comp, config) {3443 var prevConfig = comp.config;3444 comp.config = config;3445 if (_metal.core.isFunction(comp.configChanged)) {3446 comp.configChanged(config, prevConfig || {});3447 }3448 comp.emit('configChanged', {3449 prevVal: prevConfig,3450 newVal: config3451 });3452 };3453 /**3454 * Checks if the component should be updated with the current state changes.3455 * Can be overridden by subclasses or implemented by components to provide3456 * customized behavior (only updating when a state property used by the3457 * template changes, for example).3458 * @param {!Object} changes3459 * @return {boolean}3460 */3461 IncrementalDomRenderer.prototype.shouldUpdate = function shouldUpdate(changes) {3462 if (this.component_.shouldUpdate) {3463 return this.component_.shouldUpdate(changes);3464 }3465 return true;3466 };3467 /**3468 * Stores the component that has just started being rendered.3469 * @param {!Component} comp3470 */3471 IncrementalDomRenderer.startedRenderingComponent = function startedRenderingComponent(comp) {3472 renderingComponents_.push(comp);3473 };3474 /**3475 * Patches the component's element with the incremental dom function calls3476 * done by `renderIncDom`.3477 */3478 IncrementalDomRenderer.prototype.patch = function patch() {3479 if (!this.component_.element && this.parent_) {3480 // If the component has no content but was rendered from another component,3481 // we'll need to patch this parent to make sure that any new content will3482 // be added in the right place.3483 this.parent_.getRenderer().patch();3484 return;3485 }3486 var tempParent = this.guaranteeParent_();3487 if (tempParent) {3488 IncrementalDOM.patch(tempParent, this.renderInsidePatchDontSkip_);3489 _metalDom2.default.exitDocument(this.component_.element);3490 if (this.component_.element && this.component_.inDocument) {3491 this.component_.renderElement_(this.attachData_.parent, this.attachData_.sibling);3492 }3493 } else {3494 var element = this.component_.element;3495 IncrementalDOM.patchOuter(element, this.renderInsidePatchDontSkip_);3496 if (!this.component_.element) {3497 _metalDom2.default.exitDocument(element);3498 }3499 }3500 };3501 /**3502 * Updates the renderer's component when state changes, patching its element3503 * through the incremental dom function calls done by `renderIncDom`. Makes3504 * sure that it won't cause a rerender if the only change was for the3505 * "element" property.3506 */3507 IncrementalDomRenderer.prototype.update = function update() {3508 if (this.hasChangedBesidesElement_(this.changes_) && this.shouldUpdate(this.changes_)) {3509 this.patch();3510 }3511 };3512 /**3513 * Updates this renderer's component's element with the given values, unless3514 * it has already been reached by an earlier call.3515 * @param {!Element} node3516 * @protected3517 */3518 IncrementalDomRenderer.prototype.updateElementIfNotReached_ = function updateElementIfNotReached_(node) {3519 var currComp = IncrementalDomRenderer.getComponentBeingRendered();3520 var currRenderer = currComp.getRenderer();3521 if (!currRenderer.rootElementReached_) {3522 currRenderer.rootElementReached_ = true;3523 if (currComp.element !== node) {3524 currComp.element = node;3525 }3526 }3527 };3528 /**3529 * Updates the given component's context according to the data from the3530 * component that is currently being rendered.3531 * @param {!Component} comp3532 * @protected3533 */3534 IncrementalDomRenderer.prototype.updateContext_ = function updateContext_(comp) {3535 var context = comp.context;3536 var parent = IncrementalDomRenderer.getComponentBeingRendered();3537 var childContext = parent.getChildContext ? parent.getChildContext() : {};3538 _metal.object.mixin(context, parent.context, childContext);3539 comp.context = context;3540 };3541 return IncrementalDomRenderer;3542}(_metalComponent.ComponentRenderer);3543var renderingComponents_ = [];3544var emptyChildren_ = [];3545IncrementalDomRenderer.LISTENER_REGEX = /^(?:on([A-Z]\w+))|(?:data-on(\w+))$/;3546exports.default = IncrementalDomRenderer;3547},{"./IncrementalDomAop":21,"./children/IncrementalDomChildren":23,"./cleanup/IncrementalDomUnusedComponents":24,"./incremental-dom":25,"./utils/IncrementalDomUtils":26,"metal":34,"metal-component":5,"metal-dom":9}],23:[function(require,module,exports){3548'use strict';3549Object.defineProperty(exports, "__esModule", {3550 value: true3551});3552var _metal = require('metal');3553var _metal2 = _interopRequireDefault(_metal);3554var _IncrementalDomAop = require('../IncrementalDomAop');3555var _IncrementalDomAop2 = _interopRequireDefault(_IncrementalDomAop);3556var _IncrementalDomUtils = require('../utils/IncrementalDomUtils');3557var _IncrementalDomUtils2 = _interopRequireDefault(_IncrementalDomUtils);3558function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }3559function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }3560function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }3561/**3562 * Provides helpers for capturing children elements from incremental dom calls,3563 * as well as actually rendering those captured children via incremental dom3564 * later.3565 */3566var IncrementalDomChildren = function () {3567 function IncrementalDomChildren() {3568 _classCallCheck(this, IncrementalDomChildren);3569 }3570 /**3571 * Captures all child elements from incremental dom calls.3572 * @param {!IncrementalDomRenderer} renderer The renderer that is capturing3573 * children.3574 * @param {!function} callback Function to be called when children have all3575 * been captured.3576 */3577 IncrementalDomChildren.capture = function capture(renderer, callback) {3578 renderer_ = renderer;3579 callback_ = callback;3580 tree_ = {3581 config: {3582 children: []3583 }3584 };3585 currentParent_ = tree_;3586 isCapturing_ = true;3587 _IncrementalDomAop2.default.startInterception({3588 elementClose: handleInterceptedCloseCall_,3589 elementOpen: handleInterceptedOpenCall_,3590 text: handleInterceptedTextCall_3591 });3592 };3593 /**3594 * Renders a children tree through incremental dom.3595 * @param {!{args: Array, !children: Array, isText: ?boolean}}3596 * @param {function()=} opt_skipNode Optional function that is called for3597 * each node to be rendered. If it returns true, the node will be skipped.3598 * @protected3599 */3600 IncrementalDomChildren.render = function render(tree, opt_skipNode) {3601 if (isCapturing_) {3602 // If capturing, just add the node directly to the captured tree.3603 addChildToTree(tree);3604 return;3605 }3606 if (opt_skipNode && opt_skipNode(tree)) {3607 return;3608 }3609 if (_metal2.default.isDef(tree.text)) {3610 var args = tree.args ? tree.args : [];3611 args[0] = tree.text;3612 IncrementalDOM.text.apply(null, args);3613 } else {3614 var _args = _IncrementalDomUtils2.default.buildCallFromConfig(tree.tag, tree.config);3615 IncrementalDOM.elementOpen.apply(null, _args);3616 if (tree.config.children) {3617 for (var i = 0; i < tree.config.children.length; i++) {3618 IncrementalDomChildren.render(tree.config.children[i], opt_skipNode);3619 }3620 }3621 IncrementalDOM.elementClose(tree.tag);3622 }3623 };3624 return IncrementalDomChildren;3625}();3626var callback_;3627var currentParent_;3628var isCapturing_ = false;3629var renderer_;3630var tree_;3631/**3632 * Adds a child element to the tree.3633 * @param {!Array} args The arguments passed to the incremental dom call.3634 * @param {boolean=} opt_isText Optional flag indicating if the child is a3635 * text element.3636 * @protected3637 */3638function addChildCallToTree_(args, opt_isText) {3639 var child = _defineProperty({3640 parent: currentParent_3641 }, IncrementalDomChildren.CHILD_OWNER, renderer_);3642 if (opt_isText) {3643 child.text = args[0];3644 if (args.length > 1) {3645 child.args = args;3646 }3647 } else {3648 child.tag = args[0];3649 child.config = _IncrementalDomUtils2.default.buildConfigFromCall(args);3650 if (_IncrementalDomUtils2.default.isComponentTag(child.tag)) {3651 child.config.ref = _metal2.default.isDefAndNotNull(child.config.ref) ? child.config.ref : renderer_.buildRef(args[0]);3652 }3653 child.config.children = [];3654 }3655 addChildToTree(child);3656 return child;3657}3658function addChildToTree(child) {3659 currentParent_.config.children.push(child);3660}3661/**3662 * Handles an intercepted call to the `elementClose` function from incremental3663 * dom.3664 * @protected3665 */3666function handleInterceptedCloseCall_() {3667 if (currentParent_ === tree_) {3668 _IncrementalDomAop2.default.stopInterception();3669 isCapturing_ = false;3670 callback_(tree_);3671 callback_ = null;3672 currentParent_ = null;3673 renderer_ = null;3674 tree_ = null;3675 } else {3676 currentParent_ = currentParent_.parent;3677 }3678}3679/**3680 * Handles an intercepted call to the `elementOpen` function from incremental3681 * dom.3682 * @param {!function()} originalFn The original function before interception.3683 * @protected3684 */3685function handleInterceptedOpenCall_(originalFn) {3686 for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {3687 args[_key - 1] = arguments[_key];3688 }3689 currentParent_ = addChildCallToTree_(args);3690}3691/**3692 * Handles an intercepted call to the `text` function from incremental dom.3693 * @param {!function()} originalFn The original function before interception.3694 * @protected3695 */3696function handleInterceptedTextCall_(originalFn) {3697 for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {3698 args[_key2 - 1] = arguments[_key2];3699 }3700 addChildCallToTree_(args, true);3701}3702/**3703 * Property identifying a specific object as a Metal.js child node, and3704 * pointing to the renderer instance that created it.3705 * @type {string}3706 * @static3707 */3708IncrementalDomChildren.CHILD_OWNER = '__metalChildOwner';3709exports.default = IncrementalDomChildren;3710},{"../IncrementalDomAop":21,"../utils/IncrementalDomUtils":26,"metal":34}],24:[function(require,module,exports){3711'use strict';3712Object.defineProperty(exports, "__esModule", {3713 value: true3714});3715function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }3716var comps_ = [];3717var IncrementalDomUnusedComponents = function () {3718 function IncrementalDomUnusedComponents() {3719 _classCallCheck(this, IncrementalDomUnusedComponents);3720 }3721 /**3722 * Disposes all sub components that were not rerendered since the last3723 * time this function was scheduled.3724 */3725 IncrementalDomUnusedComponents.disposeUnused = function disposeUnused() {3726 for (var i = 0; i < comps_.length; i++) {3727 if (!comps_[i].isDisposed()) {3728 var renderer = comps_[i].getRenderer();3729 if (!renderer.getParent()) {3730 // Don't let disposing cause the element to be removed, since it may3731 // be currently being reused by another component.3732 comps_[i].element = null;3733 var ref = comps_[i].config.ref;3734 var owner = renderer.getOwner();3735 if (owner.components[ref] === comps_[i]) {3736 owner.disposeSubComponents([ref]);3737 } else {3738 comps_[i].dispose();3739 }3740 }3741 }3742 }3743 comps_ = [];3744 };3745 /**3746 * Schedules the given components to be checked and disposed if not used3747 * anymore, when `IncrementalDomUnusedComponents.disposeUnused` is called.3748 * @param {!Array<!Component} comps3749 */3750 IncrementalDomUnusedComponents.schedule = function schedule(comps) {3751 for (var i = 0; i < comps.length; i++) {3752 comps[i].getRenderer().parent_ = null;3753 comps_.push(comps[i]);3754 }3755 };3756 return IncrementalDomUnusedComponents;3757}();3758exports.default = IncrementalDomUnusedComponents;3759},{}],25:[function(require,module,exports){3760'use strict';3761var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };3762/* jshint ignore:start */3763/**3764 * @license3765 * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.3766 *3767 * Licensed under the Apache License, Version 2.0 (the "License");3768 * you may not use this file except in compliance with the License.3769 * You may obtain a copy of the License at3770 *3771 * http://www.apache.org/licenses/LICENSE-2.03772 *3773 * Unless required by applicable law or agreed to in writing, software3774 * distributed under the License is distributed on an "AS-IS" BASIS,3775 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.3776 * See the License for the specific language governing permissions and3777 * limitations under the License.3778 */3779(function (global, factory) {3780 factory(global.IncrementalDOM = global.IncrementalDOM || {});3781})(window, function (exports) {3782 'use strict';3783 /**3784 * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.3785 *3786 * Licensed under the Apache License, Version 2.0 (the "License");3787 * you may not use this file except in compliance with the License.3788 * You may obtain a copy of the License at3789 *3790 * http://www.apache.org/licenses/LICENSE-2.03791 *3792 * Unless required by applicable law or agreed to in writing, software3793 * distributed under the License is distributed on an "AS-IS" BASIS,3794 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.3795 * See the License for the specific language governing permissions and3796 * limitations under the License.3797 */3798 /**3799 * A cached reference to the hasOwnProperty function.3800 */3801 var hasOwnProperty = Object.prototype.hasOwnProperty;3802 /**3803 * A constructor function that will create blank objects.3804 * @constructor3805 */3806 function Blank() {}3807 Blank.prototype = Object.create(null);3808 /**3809 * Used to prevent property collisions between our "map" and its prototype.3810 * @param {!Object<string, *>} map The map to check.3811 * @param {string} property The property to check.3812 * @return {boolean} Whether map has property.3813 */3814 var has = function has(map, property) {3815 return hasOwnProperty.call(map, property);3816 };3817 /**3818 * Creates an map object without a prototype.3819 * @return {!Object}3820 */3821 var createMap = function createMap() {3822 return new Blank();3823 };3824 /**3825 * The property name where we store Incremental DOM data.3826 */3827 var DATA_PROP = '__incrementalDOMData';3828 /**3829 * Keeps track of information needed to perform diffs for a given DOM node.3830 * @param {!string} nodeName3831 * @param {?string=} key3832 * @constructor3833 */3834 function NodeData(nodeName, key) {3835 /**3836 * The attributes and their values.3837 * @const {!Object<string, *>}3838 */3839 this.attrs = createMap();3840 /**3841 * An array of attribute name/value pairs, used for quickly diffing the3842 * incomming attributes to see if the DOM node's attributes need to be3843 * updated.3844 * @const {Array<*>}3845 */3846 this.attrsArr = [];3847 /**3848 * The incoming attributes for this Node, before they are updated.3849 * @const {!Object<string, *>}3850 */3851 this.newAttrs = createMap();3852 /**3853 * Whether or not the statics have been applied for the node yet.3854 * {boolean}3855 */3856 this.staticsApplied = false;3857 /**3858 * The key used to identify this node, used to preserve DOM nodes when they3859 * move within their parent.3860 * @const3861 */3862 this.key = key;3863 /**3864 * Keeps track of children within this node by their key.3865 * {!Object<string, !Element>}3866 */3867 this.keyMap = createMap();3868 /**3869 * Whether or not the keyMap is currently valid.3870 * @type {boolean}3871 */3872 this.keyMapValid = true;3873 /**3874 * Whether or the associated node is, or contains, a focused Element.3875 * @type {boolean}3876 */3877 this.focused = false;3878 /**3879 * The node name for this node.3880 * @const {string}3881 */3882 this.nodeName = nodeName;3883 /**3884 * @type {?string}3885 */3886 this.text = null;3887 }3888 /**3889 * Initializes a NodeData object for a Node.3890 *3891 * @param {Node} node The node to initialize data for.3892 * @param {string} nodeName The node name of node.3893 * @param {?string=} key The key that identifies the node.3894 * @return {!NodeData} The newly initialized data object3895 */3896 var initData = function initData(node, nodeName, key) {3897 var data = new NodeData(nodeName, key);3898 node[DATA_PROP] = data;3899 return data;3900 };3901 /**3902 * Retrieves the NodeData object for a Node, creating it if necessary.3903 *3904 * @param {?Node} node The Node to retrieve the data for.3905 * @return {!NodeData} The NodeData for this Node.3906 */3907 var getData = function getData(node) {3908 importNode(node);3909 return node[DATA_PROP];3910 };3911 /**3912 * Imports node and its subtree, initializing caches.3913 *3914 * @param {?Node} node The Node to import.3915 */3916 var importNode = function importNode(node) {3917 if (node[DATA_PROP]) {3918 return;3919 }3920 var nodeName = node.nodeName.toLowerCase();3921 var isElement = node instanceof Element;3922 var key = isElement ? node.getAttribute('key') : null;3923 var data = initData(node, nodeName, key);3924 if (key) {3925 getData(node.parentNode).keyMap[key] = node;3926 }3927 if (isElement) {3928 var attributes = node.attributes;3929 var attrs = data.attrs;3930 var newAttrs = data.newAttrs;3931 var attrsArr = data.attrsArr;3932 for (var i = 0; i < attributes.length; i += 1) {3933 var attr = attributes[i];3934 var name = attr.name;3935 var value = attr.value;3936 attrs[name] = value;3937 newAttrs[name] = undefined;3938 attrsArr.push(name);3939 attrsArr.push(value);3940 }3941 }3942 for (var child = node.firstChild; child; child = child.nextSibling) {3943 importNode(child);3944 }3945 };3946 /**3947 * Gets the namespace to create an element (of a given tag) in.3948 * @param {string} tag The tag to get the namespace for.3949 * @param {?Node} parent3950 * @return {?string} The namespace to create the tag in.3951 */3952 var getNamespaceForTag = function getNamespaceForTag(tag, parent) {3953 if (tag === 'svg') {3954 return 'http://www.w3.org/2000/svg';3955 }3956 if (getData(parent).nodeName === 'foreignObject') {3957 return null;3958 }3959 return parent.namespaceURI;3960 };3961 /**3962 * Creates an Element.3963 * @param {Document} doc The document with which to create the Element.3964 * @param {?Node} parent3965 * @param {string} tag The tag for the Element.3966 * @param {?string=} key A key to identify the Element.3967 * @return {!Element}3968 */3969 var createElement = function createElement(doc, parent, tag, key) {3970 var namespace = getNamespaceForTag(tag, parent);3971 var el = undefined;3972 if (namespace) {3973 el = doc.createElementNS(namespace, tag);3974 } else {3975 el = doc.createElement(tag);3976 }3977 initData(el, tag, key);3978 return el;3979 };3980 /**3981 * Creates a Text Node.3982 * @param {Document} doc The document with which to create the Element.3983 * @return {!Text}3984 */3985 var createText = function createText(doc) {3986 var node = doc.createTextNode('');3987 initData(node, '#text', null);3988 return node;3989 };3990 /**3991 * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.3992 *3993 * Licensed under the Apache License, Version 2.0 (the "License");3994 * you may not use this file except in compliance with the License.3995 * You may obtain a copy of the License at3996 *3997 * http://www.apache.org/licenses/LICENSE-2.03998 *3999 * Unless required by applicable law or agreed to in writing, software4000 * distributed under the License is distributed on an "AS-IS" BASIS,4001 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.4002 * See the License for the specific language governing permissions and4003 * limitations under the License.4004 */4005 /** @const */4006 var notifications = {4007 /**4008 * Called after patch has compleated with any Nodes that have been created4009 * and added to the DOM.4010 * @type {?function(Array<!Node>)}4011 */4012 nodesCreated: null,4013 /**4014 * Called after patch has compleated with any Nodes that have been removed4015 * from the DOM.4016 * Note it's an applications responsibility to handle any childNodes.4017 * @type {?function(Array<!Node>)}4018 */4019 nodesDeleted: null4020 };4021 /**4022 * Keeps track of the state of a patch.4023 * @constructor4024 */4025 function Context() {4026 /**4027 * @type {(Array<!Node>|undefined)}4028 */4029 this.created = notifications.nodesCreated && [];4030 /**4031 * @type {(Array<!Node>|undefined)}4032 */4033 this.deleted = notifications.nodesDeleted && [];4034 }4035 /**4036 * @param {!Node} node4037 */4038 Context.prototype.markCreated = function (node) {4039 if (this.created) {4040 this.created.push(node);4041 }4042 };4043 /**4044 * @param {!Node} node4045 */4046 Context.prototype.markDeleted = function (node) {4047 if (this.deleted) {4048 this.deleted.push(node);4049 }4050 };4051 /**4052 * Notifies about nodes that were created during the patch opearation.4053 */4054 Context.prototype.notifyChanges = function () {4055 if (this.created && this.created.length > 0) {4056 notifications.nodesCreated(this.created);4057 }4058 if (this.deleted && this.deleted.length > 0) {4059 notifications.nodesDeleted(this.deleted);4060 }4061 };4062 /**4063 * Copyright 2016 The Incremental DOM Authors. All Rights Reserved.4064 *4065 * Licensed under the Apache License, Version 2.0 (the "License");4066 * you may not use this file except in compliance with the License.4067 * You may obtain a copy of the License at4068 *4069 * http://www.apache.org/licenses/LICENSE-2.04070 *4071 * Unless required by applicable law or agreed to in writing, software4072 * distributed under the License is distributed on an "AS-IS" BASIS,4073 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.4074 * See the License for the specific language governing permissions and4075 * limitations under the License.4076 */4077 /**4078 * @param {!Node} node4079 * @return {boolean} True if the node the root of a document, false otherwise.4080 */4081 var isDocumentRoot = function isDocumentRoot(node) {4082 // For ShadowRoots, check if they are a DocumentFragment instead of if they4083 // are a ShadowRoot so that this can work in 'use strict' if ShadowRoots are4084 // not supported.4085 return node instanceof Document || node instanceof DocumentFragment;4086 };4087 /**4088 * @param {!Node} node The node to start at, inclusive.4089 * @param {?Node} root The root ancestor to get until, exclusive.4090 * @return {!Array<!Node>} The ancestry of DOM nodes.4091 */4092 var getAncestry = function getAncestry(node, root) {4093 var ancestry = [];4094 var cur = node;4095 while (cur !== root) {4096 ancestry.push(cur);4097 cur = cur.parentNode;4098 }4099 return ancestry;4100 };4101 /**4102 * @param {!Node} node4103 * @return {!Node} The root node of the DOM tree that contains node.4104 */4105 var getRoot = function getRoot(node) {4106 var cur = node;4107 var prev = cur;4108 while (cur) {4109 prev = cur;4110 cur = cur.parentNode;4111 }4112 return prev;4113 };4114 /**4115 * @param {!Node} node The node to get the activeElement for.4116 * @return {?Element} The activeElement in the Document or ShadowRoot4117 * corresponding to node, if present.4118 */4119 var getActiveElement = function getActiveElement(node) {4120 var root = getRoot(node);4121 return isDocumentRoot(root) ? root.activeElement : null;4122 };4123 /**4124 * Gets the path of nodes that contain the focused node in the same document as4125 * a reference node, up until the root.4126 * @param {!Node} node The reference node to get the activeElement for.4127 * @param {?Node} root The root to get the focused path until.4128 * @return {!Array<Node>}4129 */4130 var getFocusedPath = function getFocusedPath(node, root) {4131 var activeElement = getActiveElement(node);4132 if (!activeElement || !node.contains(activeElement)) {4133 return [];4134 }4135 return getAncestry(activeElement, root);4136 };4137 /**4138 * Like insertBefore, but instead instead of moving the desired node, instead4139 * moves all the other nodes after.4140 * @param {?Node} parentNode4141 * @param {!Node} node4142 * @param {?Node} referenceNode4143 */4144 var moveBefore = function moveBefore(parentNode, node, referenceNode) {4145 var insertReferenceNode = node.nextSibling;4146 var cur = referenceNode;4147 while (cur !== node) {4148 var next = cur.nextSibling;4149 parentNode.insertBefore(cur, insertReferenceNode);4150 cur = next;4151 }4152 };4153 /** @type {?Context} */4154 var context = null;4155 /** @type {?Node} */4156 var currentNode = null;4157 /** @type {?Node} */4158 var currentParent = null;4159 /** @type {?Document} */4160 var doc = null;4161 /**4162 * @param {!Array<Node>} focusPath The nodes to mark.4163 * @param {boolean} focused Whether or not they are focused.4164 */4165 var markFocused = function markFocused(focusPath, focused) {4166 for (var i = 0; i < focusPath.length; i += 1) {4167 getData(focusPath[i]).focused = focused;4168 }4169 };4170 /**4171 * Returns a patcher function that sets up and restores a patch context,4172 * running the run function with the provided data.4173 * @param {function((!Element|!DocumentFragment),!function(T),T=): ?Node} run4174 * @return {function((!Element|!DocumentFragment),!function(T),T=): ?Node}4175 * @template T4176 */4177 var patchFactory = function patchFactory(run) {4178 /**4179 * TODO(moz): These annotations won't be necessary once we switch to Closure4180 * Compiler's new type inference. Remove these once the switch is done.4181 *4182 * @param {(!Element|!DocumentFragment)} node4183 * @param {!function(T)} fn4184 * @param {T=} data4185 * @return {?Node} node4186 * @template T4187 */4188 var f = function f(node, fn, data) {4189 var prevContext = context;4190 var prevDoc = doc;4191 var prevCurrentNode = currentNode;4192 var prevCurrentParent = currentParent;4193 var previousInAttributes = false;4194 var previousInSkip = false;4195 context = new Context();4196 doc = node.ownerDocument;4197 currentParent = node.parentNode;4198 if ('production' !== 'production') {}4199 var focusPath = getFocusedPath(node, currentParent);4200 markFocused(focusPath, true);4201 var retVal = run(node, fn, data);4202 markFocused(focusPath, false);4203 if ('production' !== 'production') {}4204 context.notifyChanges();4205 context = prevContext;4206 doc = prevDoc;4207 currentNode = prevCurrentNode;4208 currentParent = prevCurrentParent;4209 return retVal;4210 };4211 return f;4212 };4213 /**4214 * Patches the document starting at node with the provided function. This4215 * function may be called during an existing patch operation.4216 * @param {!Element|!DocumentFragment} node The Element or Document4217 * to patch.4218 * @param {!function(T)} fn A function containing elementOpen/elementClose/etc.4219 * calls that describe the DOM.4220 * @param {T=} data An argument passed to fn to represent DOM state.4221 * @return {!Node} The patched node.4222 * @template T4223 */4224 var patchInner = patchFactory(function (node, fn, data) {4225 currentNode = node;4226 enterNode();4227 fn(data);4228 exitNode();4229 if ('production' !== 'production') {}4230 return node;4231 });4232 /**4233 * Patches an Element with the the provided function. Exactly one top level4234 * element call should be made corresponding to `node`.4235 * @param {!Element} node The Element where the patch should start.4236 * @param {!function(T)} fn A function containing elementOpen/elementClose/etc.4237 * calls that describe the DOM. This should have at most one top level4238 * element call.4239 * @param {T=} data An argument passed to fn to represent DOM state.4240 * @return {?Node} The node if it was updated, its replacedment or null if it4241 * was removed.4242 * @template T4243 */4244 var patchOuter = patchFactory(function (node, fn, data) {4245 var startNode = /** @type {!Element} */{ nextSibling: node };4246 var expectedNextNode = null;4247 var expectedPrevNode = null;4248 if ('production' !== 'production') {}4249 currentNode = startNode;4250 fn(data);4251 if ('production' !== 'production') {}4252 if (node !== currentNode) {4253 removeChild(currentParent, node, getData(currentParent).keyMap);4254 }4255 return startNode === currentNode ? null : currentNode;4256 });4257 /**4258 * Checks whether or not the current node matches the specified nodeName and4259 * key.4260 *4261 * @param {!Node} matchNode A node to match the data to.4262 * @param {?string} nodeName The nodeName for this node.4263 * @param {?string=} key An optional key that identifies a node.4264 * @return {boolean} True if the node matches, false otherwise.4265 */4266 var matches = function matches(matchNode, nodeName, key) {4267 var data = getData(matchNode);4268 // Key check is done using double equals as we want to treat a null key the4269 // same as undefined. This should be okay as the only values allowed are4270 // strings, null and undefined so the == semantics are not too weird.4271 return nodeName === data.nodeName && key == data.key;4272 };4273 /**4274 * Aligns the virtual Element definition with the actual DOM, moving the4275 * corresponding DOM node to the correct location or creating it if necessary.4276 * @param {string} nodeName For an Element, this should be a valid tag string.4277 * For a Text, this should be #text.4278 * @param {?string=} key The key used to identify this element.4279 */4280 var alignWithDOM = function alignWithDOM(nodeName, key) {4281 if (currentNode && matches(currentNode, nodeName, key)) {4282 return;4283 }4284 var parentData = getData(currentParent);4285 var currentNodeData = currentNode && getData(currentNode);4286 var keyMap = parentData.keyMap;4287 var node = undefined;4288 // Check to see if the node has moved within the parent.4289 if (key) {4290 var keyNode = keyMap[key];4291 if (keyNode) {4292 if (matches(keyNode, nodeName, key)) {4293 node = keyNode;4294 } else if (keyNode === currentNode) {4295 context.markDeleted(keyNode);4296 } else {4297 removeChild(currentParent, keyNode, keyMap);4298 }4299 }4300 }4301 // Create the node if it doesn't exist.4302 if (!node) {4303 if (nodeName === '#text') {4304 node = createText(doc);4305 } else {4306 node = createElement(doc, currentParent, nodeName, key);4307 }4308 if (key) {4309 keyMap[key] = node;4310 }4311 context.markCreated(node);4312 }4313 // Re-order the node into the right position, preserving focus if either4314 // node or currentNode are focused by making sure that they are not detached4315 // from the DOM.4316 if (getData(node).focused) {4317 // Move everything else before the node.4318 moveBefore(currentParent, node, currentNode);4319 } else if (currentNodeData && currentNodeData.key && !currentNodeData.focused) {4320 // Remove the currentNode, which can always be added back since we hold a4321 // reference through the keyMap. This prevents a large number of moves when4322 // a keyed item is removed or moved backwards in the DOM.4323 currentParent.replaceChild(node, currentNode);4324 parentData.keyMapValid = false;4325 } else {4326 currentParent.insertBefore(node, currentNode);4327 }4328 currentNode = node;4329 };4330 /**4331 * @param {?Node} node4332 * @param {?Node} child4333 * @param {?Object<string, !Element>} keyMap4334 */4335 var removeChild = function removeChild(node, child, keyMap) {4336 node.removeChild(child);4337 context.markDeleted( /** @type {!Node}*/child);4338 var key = getData(child).key;4339 if (key) {4340 delete keyMap[key];4341 }4342 };4343 /**4344 * Clears out any unvisited Nodes, as the corresponding virtual element4345 * functions were never called for them.4346 */4347 var clearUnvisitedDOM = function clearUnvisitedDOM() {4348 var node = currentParent;4349 var data = getData(node);4350 var keyMap = data.keyMap;4351 var keyMapValid = data.keyMapValid;4352 var child = node.lastChild;4353 var key = undefined;4354 if (child === currentNode && keyMapValid) {4355 return;4356 }4357 while (child !== currentNode) {4358 removeChild(node, child, keyMap);4359 child = node.lastChild;4360 }4361 // Clean the keyMap, removing any unusued keys.4362 if (!keyMapValid) {4363 for (key in keyMap) {4364 child = keyMap[key];4365 if (child.parentNode !== node) {4366 context.markDeleted(child);4367 delete keyMap[key];4368 }4369 }4370 data.keyMapValid = true;4371 }4372 };4373 /**4374 * Changes to the first child of the current node.4375 */4376 var enterNode = function enterNode() {4377 currentParent = currentNode;4378 currentNode = null;4379 };4380 /**4381 * @return {?Node} The next Node to be patched.4382 */4383 var getNextNode = function getNextNode() {4384 if (currentNode) {4385 return currentNode.nextSibling;4386 } else {4387 return currentParent.firstChild;4388 }4389 };4390 /**4391 * Changes to the next sibling of the current node.4392 */4393 var nextNode = function nextNode() {4394 currentNode = getNextNode();4395 };4396 /**4397 * Changes to the parent of the current node, removing any unvisited children.4398 */4399 var exitNode = function exitNode() {4400 clearUnvisitedDOM();4401 currentNode = currentParent;4402 currentParent = currentParent.parentNode;4403 };4404 /**4405 * Makes sure that the current node is an Element with a matching tagName and4406 * key.4407 *4408 * @param {string} tag The element's tag.4409 * @param {?string=} key The key used to identify this element. This can be an4410 * empty string, but performance may be better if a unique value is used4411 * when iterating over an array of items.4412 * @return {!Element} The corresponding Element.4413 */4414 var coreElementOpen = function coreElementOpen(tag, key) {4415 nextNode();4416 alignWithDOM(tag, key);4417 enterNode();4418 return (/** @type {!Element} */currentParent4419 );4420 };4421 /**4422 * Closes the currently open Element, removing any unvisited children if4423 * necessary.4424 *4425 * @return {!Element} The corresponding Element.4426 */4427 var coreElementClose = function coreElementClose() {4428 if ('production' !== 'production') {}4429 exitNode();4430 return (/** @type {!Element} */currentNode4431 );4432 };4433 /**4434 * Makes sure the current node is a Text node and creates a Text node if it is4435 * not.4436 *4437 * @return {!Text} The corresponding Text Node.4438 */4439 var coreText = function coreText() {4440 nextNode();4441 alignWithDOM('#text', null);4442 return (/** @type {!Text} */currentNode4443 );4444 };4445 /**4446 * Gets the current Element being patched.4447 * @return {!Element}4448 */4449 var currentElement = function currentElement() {4450 if ('production' !== 'production') {}4451 return (/** @type {!Element} */currentParent4452 );4453 };4454 /**4455 * @return {Node} The Node that will be evaluated for the next instruction.4456 */4457 var currentPointer = function currentPointer() {4458 if ('production' !== 'production') {}4459 return getNextNode();4460 };4461 /**4462 * Skips the children in a subtree, allowing an Element to be closed without4463 * clearing out the children.4464 */4465 var skip = function skip() {4466 if ('production' !== 'production') {}4467 currentNode = currentParent.lastChild;4468 };4469 /**4470 * Skips the next Node to be patched, moving the pointer forward to the next4471 * sibling of the current pointer.4472 */4473 var skipNode = nextNode;4474 /**4475 * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.4476 *4477 * Licensed under the Apache License, Version 2.0 (the "License");4478 * you may not use this file except in compliance with the License.4479 * You may obtain a copy of the License at4480 *4481 * http://www.apache.org/licenses/LICENSE-2.04482 *4483 * Unless required by applicable law or agreed to in writing, software4484 * distributed under the License is distributed on an "AS-IS" BASIS,4485 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.4486 * See the License for the specific language governing permissions and4487 * limitations under the License.4488 */4489 /** @const */4490 var symbols = {4491 default: '__default'4492 };4493 /**4494 * @param {string} name4495 * @return {string|undefined} The namespace to use for the attribute.4496 */4497 var getNamespace = function getNamespace(name) {4498 if (name.lastIndexOf('xml:', 0) === 0) {4499 return 'http://www.w3.org/XML/1998/namespace';4500 }4501 if (name.lastIndexOf('xlink:', 0) === 0) {4502 return 'http://www.w3.org/1999/xlink';4503 }4504 };4505 /**4506 * Applies an attribute or property to a given Element. If the value is null4507 * or undefined, it is removed from the Element. Otherwise, the value is set4508 * as an attribute.4509 * @param {!Element} el4510 * @param {string} name The attribute's name.4511 * @param {?(boolean|number|string)=} value The attribute's value.4512 */4513 var applyAttr = function applyAttr(el, name, value) {4514 if (value == null) {4515 el.removeAttribute(name);4516 } else {4517 var attrNS = getNamespace(name);4518 if (attrNS) {4519 el.setAttributeNS(attrNS, name, value);4520 } else {4521 el.setAttribute(name, value);4522 }4523 }4524 };4525 /**4526 * Applies a property to a given Element.4527 * @param {!Element} el4528 * @param {string} name The property's name.4529 * @param {*} value The property's value.4530 */4531 var applyProp = function applyProp(el, name, value) {4532 el[name] = value;4533 };4534 /**4535 * Applies a value to a style declaration. Supports CSS custom properties by4536 * setting properties containing a dash using CSSStyleDeclaration.setProperty.4537 * @param {CSSStyleDeclaration} style4538 * @param {!string} prop4539 * @param {*} value4540 */4541 var setStyleValue = function setStyleValue(style, prop, value) {4542 if (prop.indexOf('-') >= 0) {4543 style.setProperty(prop, /** @type {string} */value);4544 } else {4545 style[prop] = value;4546 }4547 };4548 /**4549 * Applies a style to an Element. No vendor prefix expansion is done for4550 * property names/values.4551 * @param {!Element} el4552 * @param {string} name The attribute's name.4553 * @param {*} style The style to set. Either a string of css or an object4554 * containing property-value pairs.4555 */4556 var applyStyle = function applyStyle(el, name, style) {4557 if (typeof style === 'string') {4558 el.style.cssText = style;4559 } else {4560 el.style.cssText = '';4561 var elStyle = el.style;4562 var obj = /** @type {!Object<string,string>} */style;4563 for (var prop in obj) {4564 if (has(obj, prop)) {4565 setStyleValue(elStyle, prop, obj[prop]);4566 }4567 }4568 }4569 };4570 /**4571 * Updates a single attribute on an Element.4572 * @param {!Element} el4573 * @param {string} name The attribute's name.4574 * @param {*} value The attribute's value. If the value is an object or4575 * function it is set on the Element, otherwise, it is set as an HTML4576 * attribute.4577 */4578 var applyAttributeTyped = function applyAttributeTyped(el, name, value) {4579 var type = typeof value === 'undefined' ? 'undefined' : _typeof(value);4580 if (type === 'object' || type === 'function') {4581 applyProp(el, name, value);4582 } else {4583 applyAttr(el, name, /** @type {?(boolean|number|string)} */value);4584 }4585 };4586 /**4587 * Calls the appropriate attribute mutator for this attribute.4588 * @param {!Element} el4589 * @param {string} name The attribute's name.4590 * @param {*} value The attribute's value.4591 */4592 var updateAttribute = function updateAttribute(el, name, value) {4593 var data = getData(el);4594 var attrs = data.attrs;4595 if (attrs[name] === value) {4596 return;4597 }4598 var mutator = attributes[name] || attributes[symbols.default];4599 mutator(el, name, value);4600 attrs[name] = value;4601 };4602 /**4603 * A publicly mutable object to provide custom mutators for attributes.4604 * @const {!Object<string, function(!Element, string, *)>}4605 */4606 var attributes = createMap();4607 // Special generic mutator that's called for any attribute that does not4608 // have a specific mutator.4609 attributes[symbols.default] = applyAttributeTyped;4610 attributes['style'] = applyStyle;4611 /**4612 * The offset in the virtual element declaration where the attributes are4613 * specified.4614 * @const4615 */4616 var ATTRIBUTES_OFFSET = 3;4617 /**4618 * Builds an array of arguments for use with elementOpenStart, attr and4619 * elementOpenEnd.4620 * @const {Array<*>}4621 */4622 var argsBuilder = [];4623 /**4624 * @param {string} tag The element's tag.4625 * @param {?string=} key The key used to identify this element. This can be an4626 * empty string, but performance may be better if a unique value is used4627 * when iterating over an array of items.4628 * @param {?Array<*>=} statics An array of attribute name/value pairs of the4629 * static attributes for the Element. These will only be set once when the4630 * Element is created.4631 * @param {...*} var_args, Attribute name/value pairs of the dynamic attributes4632 * for the Element.4633 * @return {!Element} The corresponding Element.4634 */4635 var elementOpen = function elementOpen(tag, key, statics, var_args) {4636 if ('production' !== 'production') {}4637 var node = coreElementOpen(tag, key);4638 var data = getData(node);4639 if (!data.staticsApplied) {4640 if (statics) {4641 for (var _i = 0; _i < statics.length; _i += 2) {4642 var name = /** @type {string} */statics[_i];4643 var value = statics[_i + 1];4644 updateAttribute(node, name, value);4645 }4646 }4647 // Down the road, we may want to keep track of the statics array to use it4648 // as an additional signal about whether a node matches or not. For now,4649 // just use a marker so that we do not reapply statics.4650 data.staticsApplied = true;4651 }4652 /*4653 * Checks to see if one or more attributes have changed for a given Element.4654 * When no attributes have changed, this is much faster than checking each4655 * individual argument. When attributes have changed, the overhead of this is4656 * minimal.4657 */4658 var attrsArr = data.attrsArr;4659 var newAttrs = data.newAttrs;4660 var isNew = !attrsArr.length;4661 var i = ATTRIBUTES_OFFSET;4662 var j = 0;4663 for (; i < arguments.length; i += 2, j += 2) {4664 var _attr = arguments[i];4665 if (isNew) {4666 attrsArr[j] = _attr;4667 newAttrs[_attr] = undefined;4668 } else if (attrsArr[j] !== _attr) {4669 break;4670 }4671 var value = arguments[i + 1];4672 if (isNew || attrsArr[j + 1] !== value) {4673 attrsArr[j + 1] = value;4674 updateAttribute(node, _attr, value);4675 }4676 }4677 if (i < arguments.length || j < attrsArr.length) {4678 for (; i < arguments.length; i += 1, j += 1) {4679 attrsArr[j] = arguments[i];4680 }4681 if (j < attrsArr.length) {4682 attrsArr.length = j;4683 }4684 /*4685 * Actually perform the attribute update.4686 */4687 for (i = 0; i < attrsArr.length; i += 2) {4688 var name = /** @type {string} */attrsArr[i];4689 var value = attrsArr[i + 1];4690 newAttrs[name] = value;4691 }4692 for (var _attr2 in newAttrs) {4693 updateAttribute(node, _attr2, newAttrs[_attr2]);4694 newAttrs[_attr2] = undefined;4695 }4696 }4697 return node;4698 };4699 /**4700 * Declares a virtual Element at the current location in the document. This4701 * corresponds to an opening tag and a elementClose tag is required. This is4702 * like elementOpen, but the attributes are defined using the attr function4703 * rather than being passed as arguments. Must be folllowed by 0 or more calls4704 * to attr, then a call to elementOpenEnd.4705 * @param {string} tag The element's tag.4706 * @param {?string=} key The key used to identify this element. This can be an4707 * empty string, but performance may be better if a unique value is used4708 * when iterating over an array of items.4709 * @param {?Array<*>=} statics An array of attribute name/value pairs of the4710 * static attributes for the Element. These will only be set once when the4711 * Element is created.4712 */4713 var elementOpenStart = function elementOpenStart(tag, key, statics) {4714 if ('production' !== 'production') {}4715 argsBuilder[0] = tag;4716 argsBuilder[1] = key;4717 argsBuilder[2] = statics;4718 };4719 /***4720 * Defines a virtual attribute at this point of the DOM. This is only valid4721 * when called between elementOpenStart and elementOpenEnd.4722 *4723 * @param {string} name4724 * @param {*} value4725 */4726 var attr = function attr(name, value) {4727 if ('production' !== 'production') {}4728 argsBuilder.push(name);4729 argsBuilder.push(value);4730 };4731 /**4732 * Closes an open tag started with elementOpenStart.4733 * @return {!Element} The corresponding Element.4734 */4735 var elementOpenEnd = function elementOpenEnd() {4736 if ('production' !== 'production') {}4737 var node = elementOpen.apply(null, argsBuilder);4738 argsBuilder.length = 0;4739 return node;4740 };4741 /**4742 * Closes an open virtual Element.4743 *4744 * @param {string} tag The element's tag.4745 * @return {!Element} The corresponding Element.4746 */4747 var elementClose = function elementClose(tag) {4748 if ('production' !== 'production') {}4749 var node = coreElementClose();4750 if ('production' !== 'production') {}4751 return node;4752 };4753 /**4754 * Declares a virtual Element at the current location in the document that has4755 * no children.4756 * @param {string} tag The element's tag.4757 * @param {?string=} key The key used to identify this element. This can be an4758 * empty string, but performance may be better if a unique value is used4759 * when iterating over an array of items.4760 * @param {?Array<*>=} statics An array of attribute name/value pairs of the4761 * static attributes for the Element. These will only be set once when the4762 * Element is created.4763 * @param {...*} var_args Attribute name/value pairs of the dynamic attributes4764 * for the Element.4765 * @return {!Element} The corresponding Element.4766 */4767 var elementVoid = function elementVoid(tag, key, statics, var_args) {4768 elementOpen.apply(null, arguments);4769 return elementClose(tag);4770 };4771 /**4772 * Declares a virtual Text at this point in the document.4773 *4774 * @param {string|number|boolean} value The value of the Text.4775 * @param {...(function((string|number|boolean)):string)} var_args4776 * Functions to format the value which are called only when the value has4777 * changed.4778 * @return {!Text} The corresponding text node.4779 */4780 var text = function text(value, var_args) {4781 if ('production' !== 'production') {}4782 var node = coreText();4783 var data = getData(node);4784 if (data.text !== value) {4785 data.text = /** @type {string} */value;4786 var formatted = value;4787 for (var i = 1; i < arguments.length; i += 1) {4788 /*4789 * Call the formatter function directly to prevent leaking arguments.4790 * https://github.com/google/incremental-dom/pull/204#issuecomment-1782235744791 */4792 var fn = arguments[i];4793 formatted = fn(formatted);4794 }4795 node.data = formatted;4796 }4797 return node;4798 };4799 exports.patch = patchInner;4800 exports.patchInner = patchInner;4801 exports.patchOuter = patchOuter;4802 exports.currentElement = currentElement;4803 exports.currentPointer = currentPointer;4804 exports.skip = skip;4805 exports.skipNode = skipNode;4806 exports.elementVoid = elementVoid;4807 exports.elementOpenStart = elementOpenStart;4808 exports.elementOpenEnd = elementOpenEnd;4809 exports.elementOpen = elementOpen;4810 exports.elementClose = elementClose;4811 exports.text = text;4812 exports.attr = attr;4813 exports.symbols = symbols;4814 exports.attributes = attributes;4815 exports.applyAttr = applyAttr;4816 exports.applyProp = applyProp;4817 exports.notifications = notifications;4818 exports.importNode = importNode;4819});4820/* jshint ignore:end */4821},{}],26:[function(require,module,exports){4822'use strict';4823Object.defineProperty(exports, "__esModule", {4824 value: true4825});4826var _metal = require('metal');4827var _metal2 = _interopRequireDefault(_metal);4828function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }4829function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }4830/**4831 * Utility functions used to handle incremental dom calls.4832 */4833var IncrementalDomUtils = function () {4834 function IncrementalDomUtils() {4835 _classCallCheck(this, IncrementalDomUtils);4836 }4837 /**4838 * Builds the component config object from its incremental dom call's4839 * arguments.4840 * @param {!Array} args4841 * @return {!Object}4842 */4843 IncrementalDomUtils.buildConfigFromCall = function buildConfigFromCall(args) {4844 var config = {};4845 if (args[1]) {4846 config.key = args[1];4847 }4848 var attrsArr = (args[2] || []).concat(args.slice(3));4849 for (var i = 0; i < attrsArr.length; i += 2) {4850 config[attrsArr[i]] = attrsArr[i + 1];4851 }4852 return config;4853 };4854 /**4855 * Builds an incremental dom call array from the given tag and config object.4856 * @param {string} tag4857 * @param {!Object} config4858 * @return {!Array}4859 */4860 IncrementalDomUtils.buildCallFromConfig = function buildCallFromConfig(tag, config) {4861 var call = [tag, config.key, []];4862 var keys = Object.keys(config);4863 for (var i = 0; i < keys.length; i++) {4864 if (keys[i] !== 'children') {4865 call.push(keys[i], config[keys[i]]);4866 }4867 }4868 return call;4869 };4870 /**4871 * Checks if the given tag represents a metal component.4872 * @param {string} tag4873 * @param {boolean}4874 */4875 IncrementalDomUtils.isComponentTag = function isComponentTag(tag) {4876 return !_metal2.default.isString(tag) || tag[0] === tag[0].toUpperCase();4877 };4878 return IncrementalDomUtils;4879}();4880exports.default = IncrementalDomUtils;4881},{"metal":34}],27:[function(require,module,exports){4882'use strict';4883Object.defineProperty(exports, "__esModule", {4884 value: true4885});4886require('./iDOMHelpers');4887var _metalComponent = require('metal-component');4888var _metalComponent2 = _interopRequireDefault(_metalComponent);4889var _metalIncrementalDom = require('metal-incremental-dom');...
compiler-core.cjs.js
Source:compiler-core.cjs.js
...3726};3727function resolveComponentType(node, context, ssr = false) {3728 const { tag } = node;3729 // 1. dynamic component3730 const isProp = isComponentTag(tag)3731 ? findProp(node, 'is')3732 : findDir(node, 'is');3733 if (isProp) {3734 const exp = isProp.type === 6 /* ATTRIBUTE */3735 ? isProp.value && createSimpleExpression(isProp.value.content, true)3736 : isProp.exp;3737 if (exp) {3738 return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [3739 exp3740 ]);3741 }3742 }3743 // 2. built-in components (Teleport, Transition, KeepAlive, Suspense...)3744 const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag);3745 if (builtIn) {3746 // built-ins are simply fallthroughs / have special handling during ssr3747 // so we don't need to import their runtime equivalents3748 if (!ssr)3749 context.helper(builtIn);3750 return builtIn;3751 }3752 // 3. user component (from setup bindings)3753 // this is skipped in browser build since browser builds do not perform3754 // binding analysis.3755 {3756 const fromSetup = resolveSetupReference(tag, context);3757 if (fromSetup) {3758 return fromSetup;3759 }3760 }3761 // 4. Self referencing component (inferred from filename)3762 if (context.selfName &&3763 shared.capitalize(shared.camelize(tag)) === context.selfName) {3764 context.helper(RESOLVE_COMPONENT);3765 // codegen.ts has special check for __self postfix when generating3766 // component imports, which will pass additional `maybeSelfReference` flag3767 // to `resolveComponent`.3768 context.components.add(tag + `__self`);3769 return toValidAssetId(tag, `component`);3770 }3771 // 5. user component (resolve)3772 context.helper(RESOLVE_COMPONENT);3773 context.components.add(tag);3774 return toValidAssetId(tag, `component`);3775}3776function resolveSetupReference(name, context) {3777 const bindings = context.bindingMetadata;3778 if (!bindings || bindings.__isScriptSetup === false) {3779 return;3780 }3781 const camelName = shared.camelize(name);3782 const PascalName = shared.capitalize(camelName);3783 const checkType = (type) => {3784 if (bindings[name] === type) {3785 return name;3786 }3787 if (bindings[camelName] === type) {3788 return camelName;3789 }3790 if (bindings[PascalName] === type) {3791 return PascalName;3792 }3793 };3794 const fromConst = checkType("setup-const" /* SETUP_CONST */);3795 if (fromConst) {3796 return context.inline3797 ? // in inline mode, const setup bindings (e.g. imports) can be used as-is3798 fromConst3799 : `$setup[${JSON.stringify(fromConst)}]`;3800 }3801 const fromMaybeRef = checkType("setup-let" /* SETUP_LET */) ||3802 checkType("setup-ref" /* SETUP_REF */) ||3803 checkType("setup-maybe-ref" /* SETUP_MAYBE_REF */);3804 if (fromMaybeRef) {3805 return context.inline3806 ? // setup scope bindings that may be refs need to be unrefed3807 `${context.helperString(UNREF)}(${fromMaybeRef})`3808 : `$setup[${JSON.stringify(fromMaybeRef)}]`;3809 }3810}3811function buildProps(node, context, props = node.props, ssr = false) {3812 const { tag, loc: elementLoc } = node;3813 const isComponent = node.tagType === 1 /* COMPONENT */;3814 let properties = [];3815 const mergeArgs = [];3816 const runtimeDirectives = [];3817 // patchFlag analysis3818 let patchFlag = 0;3819 let hasRef = false;3820 let hasClassBinding = false;3821 let hasStyleBinding = false;3822 let hasHydrationEventBinding = false;3823 let hasDynamicKeys = false;3824 let hasVnodeHook = false;3825 const dynamicPropNames = [];3826 const analyzePatchFlag = ({ key, value }) => {3827 if (isStaticExp(key)) {3828 const name = key.content;3829 const isEventHandler = shared.isOn(name);3830 if (!isComponent &&3831 isEventHandler &&3832 // omit the flag for click handlers because hydration gives click3833 // dedicated fast path.3834 name.toLowerCase() !== 'onclick' &&3835 // omit v-model handlers3836 name !== 'onUpdate:modelValue' &&3837 // omit onVnodeXXX hooks3838 !shared.isReservedProp(name)) {3839 hasHydrationEventBinding = true;3840 }3841 if (isEventHandler && shared.isReservedProp(name)) {3842 hasVnodeHook = true;3843 }3844 if (value.type === 20 /* JS_CACHE_EXPRESSION */ ||3845 ((value.type === 4 /* SIMPLE_EXPRESSION */ ||3846 value.type === 8 /* COMPOUND_EXPRESSION */) &&3847 getConstantType(value, context) > 0)) {3848 // skip if the prop is a cached handler or has constant value3849 return;3850 }3851 if (name === 'ref') {3852 hasRef = true;3853 }3854 else if (name === 'class' && !isComponent) {3855 hasClassBinding = true;3856 }3857 else if (name === 'style' && !isComponent) {3858 hasStyleBinding = true;3859 }3860 else if (name !== 'key' && !dynamicPropNames.includes(name)) {3861 dynamicPropNames.push(name);3862 }3863 }3864 else {3865 hasDynamicKeys = true;3866 }3867 };3868 for (let i = 0; i < props.length; i++) {3869 // static attribute3870 const prop = props[i];3871 if (prop.type === 6 /* ATTRIBUTE */) {3872 const { loc, name, value } = prop;3873 let isStatic = true;3874 if (name === 'ref') {3875 hasRef = true;3876 // in inline mode there is no setupState object, so we can't use string3877 // keys to set the ref. Instead, we need to transform it to pass the3878 // acrtual ref instead.3879 if (context.inline) {3880 isStatic = false;3881 }3882 }3883 // skip :is on <component>3884 if (name === 'is' && isComponentTag(tag)) {3885 continue;3886 }3887 properties.push(createObjectProperty(createSimpleExpression(name, true, getInnerRange(loc, 0, name.length)), createSimpleExpression(value ? value.content : '', isStatic, value ? value.loc : loc)));3888 }3889 else {3890 // directives3891 const { name, arg, exp, loc } = prop;3892 const isBind = name === 'bind';3893 const isOn = name === 'on';3894 // skip v-slot - it is handled by its dedicated transform.3895 if (name === 'slot') {3896 if (!isComponent) {3897 context.onError(createCompilerError(39 /* X_V_SLOT_MISPLACED */, loc));3898 }3899 continue;3900 }3901 // skip v-once - it is handled by its dedicated transform.3902 if (name === 'once') {3903 continue;3904 }3905 // skip v-is and :is on <component>3906 if (name === 'is' ||3907 (isBind && isComponentTag(tag) && isBindKey(arg, 'is'))) {3908 continue;3909 }3910 // skip v-on in SSR compilation3911 if (isOn && ssr) {3912 continue;3913 }3914 // special case for v-bind and v-on with no argument3915 if (!arg && (isBind || isOn)) {3916 hasDynamicKeys = true;3917 if (exp) {3918 if (properties.length) {3919 mergeArgs.push(createObjectExpression(dedupeProperties(properties), elementLoc));3920 properties = [];3921 }3922 if (isBind) {3923 mergeArgs.push(exp);3924 }3925 else {3926 // v-on="obj" -> toHandlers(obj)3927 mergeArgs.push({3928 type: 14 /* JS_CALL_EXPRESSION */,3929 loc,3930 callee: context.helper(TO_HANDLERS),3931 arguments: [exp]3932 });3933 }3934 }3935 else {3936 context.onError(createCompilerError(isBind3937 ? 33 /* X_V_BIND_NO_EXPRESSION */3938 : 34 /* X_V_ON_NO_EXPRESSION */, loc));3939 }3940 continue;3941 }3942 const directiveTransform = context.directiveTransforms[name];3943 if (directiveTransform) {3944 // has built-in directive transform.3945 const { props, needRuntime } = directiveTransform(prop, node, context);3946 !ssr && props.forEach(analyzePatchFlag);3947 properties.push(...props);3948 if (needRuntime) {3949 runtimeDirectives.push(prop);3950 if (shared.isSymbol(needRuntime)) {3951 directiveImportMap.set(prop, needRuntime);3952 }3953 }3954 }3955 else {3956 // no built-in transform, this is a user custom directive.3957 runtimeDirectives.push(prop);3958 }3959 }3960 }3961 let propsExpression = undefined;3962 // has v-bind="object" or v-on="object", wrap with mergeProps3963 if (mergeArgs.length) {3964 if (properties.length) {3965 mergeArgs.push(createObjectExpression(dedupeProperties(properties), elementLoc));3966 }3967 if (mergeArgs.length > 1) {3968 propsExpression = createCallExpression(context.helper(MERGE_PROPS), mergeArgs, elementLoc);3969 }3970 else {3971 // single v-bind with nothing else - no need for a mergeProps call3972 propsExpression = mergeArgs[0];3973 }3974 }3975 else if (properties.length) {3976 propsExpression = createObjectExpression(dedupeProperties(properties), elementLoc);3977 }3978 // patchFlag analysis3979 if (hasDynamicKeys) {3980 patchFlag |= 16 /* FULL_PROPS */;3981 }3982 else {3983 if (hasClassBinding) {3984 patchFlag |= 2 /* CLASS */;3985 }3986 if (hasStyleBinding) {3987 patchFlag |= 4 /* STYLE */;3988 }3989 if (dynamicPropNames.length) {3990 patchFlag |= 8 /* PROPS */;3991 }3992 if (hasHydrationEventBinding) {3993 patchFlag |= 32 /* HYDRATE_EVENTS */;3994 }3995 }3996 if ((patchFlag === 0 || patchFlag === 32 /* HYDRATE_EVENTS */) &&3997 (hasRef || hasVnodeHook || runtimeDirectives.length > 0)) {3998 patchFlag |= 512 /* NEED_PATCH */;3999 }4000 return {4001 props: propsExpression,4002 directives: runtimeDirectives,4003 patchFlag,4004 dynamicPropNames4005 };4006}4007// Dedupe props in an object literal.4008// Literal duplicated attributes would have been warned during the parse phase,4009// however, it's possible to encounter duplicated `onXXX` handlers with different4010// modifiers. We also need to merge static and dynamic class / style attributes.4011// - onXXX handlers / style: merge into array4012// - class: merge into single expression with concatenation4013function dedupeProperties(properties) {4014 const knownProps = new Map();4015 const deduped = [];4016 for (let i = 0; i < properties.length; i++) {4017 const prop = properties[i];4018 // dynamic keys are always allowed4019 if (prop.key.type === 8 /* COMPOUND_EXPRESSION */ || !prop.key.isStatic) {4020 deduped.push(prop);4021 continue;4022 }4023 const name = prop.key.content;4024 const existing = knownProps.get(name);4025 if (existing) {4026 if (name === 'style' || name === 'class' || name.startsWith('on')) {4027 mergeAsArray(existing, prop);4028 }4029 // unexpected duplicate, should have emitted error during parse4030 }4031 else {4032 knownProps.set(name, prop);4033 deduped.push(prop);4034 }4035 }4036 return deduped;4037}4038function mergeAsArray(existing, incoming) {4039 if (existing.value.type === 17 /* JS_ARRAY_EXPRESSION */) {4040 existing.value.elements.push(incoming.value);4041 }4042 else {4043 existing.value = createArrayExpression([existing.value, incoming.value], existing.loc);4044 }4045}4046function buildDirectiveArgs(dir, context) {4047 const dirArgs = [];4048 const runtime = directiveImportMap.get(dir);4049 if (runtime) {4050 // built-in directive with runtime4051 dirArgs.push(context.helperString(runtime));4052 }4053 else {4054 // user directive.4055 // see if we have directives exposed via <script setup>4056 const fromSetup = resolveSetupReference(dir.name, context);4057 if (fromSetup) {4058 dirArgs.push(fromSetup);4059 }4060 else {4061 // inject statement for resolving directive4062 context.helper(RESOLVE_DIRECTIVE);4063 context.directives.add(dir.name);4064 dirArgs.push(toValidAssetId(dir.name, `directive`));4065 }4066 }4067 const { loc } = dir;4068 if (dir.exp)4069 dirArgs.push(dir.exp);4070 if (dir.arg) {4071 if (!dir.exp) {4072 dirArgs.push(`void 0`);4073 }4074 dirArgs.push(dir.arg);4075 }4076 if (Object.keys(dir.modifiers).length) {4077 if (!dir.arg) {4078 if (!dir.exp) {4079 dirArgs.push(`void 0`);4080 }4081 dirArgs.push(`void 0`);4082 }4083 const trueExpression = createSimpleExpression(`true`, false, loc);4084 dirArgs.push(createObjectExpression(dir.modifiers.map(modifier => createObjectProperty(modifier, trueExpression)), loc));4085 }4086 return createArrayExpression(dirArgs, dir.loc);4087}4088function stringifyDynamicPropNames(props) {4089 let propsNamesString = `[`;4090 for (let i = 0, l = props.length; i < l; i++) {4091 propsNamesString += JSON.stringify(props[i]);4092 if (i < l - 1)4093 propsNamesString += ', ';4094 }4095 return propsNamesString + `]`;4096}4097function isComponentTag(tag) {4098 return tag[0].toLowerCase() + tag.slice(1) === 'component';4099}4100Object.freeze({})4101 ;4102Object.freeze([]) ;4103const cacheStringFunction = (fn) => {4104 const cache = Object.create(null);4105 return ((str) => {4106 const hit = cache[str];4107 return hit || (cache[str] = fn(str));4108 });4109};4110const camelizeRE = /-(\w)/g;4111/**
...
compiler-core.cjs.prod.js
Source:compiler-core.cjs.prod.js
...3652};3653function resolveComponentType(node, context, ssr = false) {3654 const { tag } = node;3655 // 1. dynamic component3656 const isProp = isComponentTag(tag)3657 ? findProp(node, 'is')3658 : findDir(node, 'is');3659 if (isProp) {3660 const exp = isProp.type === 6 /* ATTRIBUTE */3661 ? isProp.value && createSimpleExpression(isProp.value.content, true)3662 : isProp.exp;3663 if (exp) {3664 return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [3665 exp3666 ]);3667 }3668 }3669 // 2. built-in components (Teleport, Transition, KeepAlive, Suspense...)3670 const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag);3671 if (builtIn) {3672 // built-ins are simply fallthroughs / have special handling during ssr3673 // so we don't need to import their runtime equivalents3674 if (!ssr)3675 context.helper(builtIn);3676 return builtIn;3677 }3678 // 3. user component (from setup bindings)3679 // this is skipped in browser build since browser builds do not perform3680 // binding analysis.3681 {3682 const fromSetup = resolveSetupReference(tag, context);3683 if (fromSetup) {3684 return fromSetup;3685 }3686 }3687 // 4. Self referencing component (inferred from filename)3688 if (context.selfName &&3689 shared.capitalize(shared.camelize(tag)) === context.selfName) {3690 context.helper(RESOLVE_COMPONENT);3691 // codegen.ts has special check for __self postfix when generating3692 // component imports, which will pass additional `maybeSelfReference` flag3693 // to `resolveComponent`.3694 context.components.add(tag + `__self`);3695 return toValidAssetId(tag, `component`);3696 }3697 // 5. user component (resolve)3698 context.helper(RESOLVE_COMPONENT);3699 context.components.add(tag);3700 return toValidAssetId(tag, `component`);3701}3702function resolveSetupReference(name, context) {3703 const bindings = context.bindingMetadata;3704 if (!bindings || bindings.__isScriptSetup === false) {3705 return;3706 }3707 const camelName = shared.camelize(name);3708 const PascalName = shared.capitalize(camelName);3709 const checkType = (type) => {3710 if (bindings[name] === type) {3711 return name;3712 }3713 if (bindings[camelName] === type) {3714 return camelName;3715 }3716 if (bindings[PascalName] === type) {3717 return PascalName;3718 }3719 };3720 const fromConst = checkType("setup-const" /* SETUP_CONST */);3721 if (fromConst) {3722 return context.inline3723 ? // in inline mode, const setup bindings (e.g. imports) can be used as-is3724 fromConst3725 : `$setup[${JSON.stringify(fromConst)}]`;3726 }3727 const fromMaybeRef = checkType("setup-let" /* SETUP_LET */) ||3728 checkType("setup-ref" /* SETUP_REF */) ||3729 checkType("setup-maybe-ref" /* SETUP_MAYBE_REF */);3730 if (fromMaybeRef) {3731 return context.inline3732 ? // setup scope bindings that may be refs need to be unrefed3733 `${context.helperString(UNREF)}(${fromMaybeRef})`3734 : `$setup[${JSON.stringify(fromMaybeRef)}]`;3735 }3736}3737function buildProps(node, context, props = node.props, ssr = false) {3738 const { tag, loc: elementLoc } = node;3739 const isComponent = node.tagType === 1 /* COMPONENT */;3740 let properties = [];3741 const mergeArgs = [];3742 const runtimeDirectives = [];3743 // patchFlag analysis3744 let patchFlag = 0;3745 let hasRef = false;3746 let hasClassBinding = false;3747 let hasStyleBinding = false;3748 let hasHydrationEventBinding = false;3749 let hasDynamicKeys = false;3750 let hasVnodeHook = false;3751 const dynamicPropNames = [];3752 const analyzePatchFlag = ({ key, value }) => {3753 if (isStaticExp(key)) {3754 const name = key.content;3755 const isEventHandler = shared.isOn(name);3756 if (!isComponent &&3757 isEventHandler &&3758 // omit the flag for click handlers because hydration gives click3759 // dedicated fast path.3760 name.toLowerCase() !== 'onclick' &&3761 // omit v-model handlers3762 name !== 'onUpdate:modelValue' &&3763 // omit onVnodeXXX hooks3764 !shared.isReservedProp(name)) {3765 hasHydrationEventBinding = true;3766 }3767 if (isEventHandler && shared.isReservedProp(name)) {3768 hasVnodeHook = true;3769 }3770 if (value.type === 20 /* JS_CACHE_EXPRESSION */ ||3771 ((value.type === 4 /* SIMPLE_EXPRESSION */ ||3772 value.type === 8 /* COMPOUND_EXPRESSION */) &&3773 getConstantType(value, context) > 0)) {3774 // skip if the prop is a cached handler or has constant value3775 return;3776 }3777 if (name === 'ref') {3778 hasRef = true;3779 }3780 else if (name === 'class' && !isComponent) {3781 hasClassBinding = true;3782 }3783 else if (name === 'style' && !isComponent) {3784 hasStyleBinding = true;3785 }3786 else if (name !== 'key' && !dynamicPropNames.includes(name)) {3787 dynamicPropNames.push(name);3788 }3789 }3790 else {3791 hasDynamicKeys = true;3792 }3793 };3794 for (let i = 0; i < props.length; i++) {3795 // static attribute3796 const prop = props[i];3797 if (prop.type === 6 /* ATTRIBUTE */) {3798 const { loc, name, value } = prop;3799 let isStatic = true;3800 if (name === 'ref') {3801 hasRef = true;3802 // in inline mode there is no setupState object, so we can't use string3803 // keys to set the ref. Instead, we need to transform it to pass the3804 // acrtual ref instead.3805 if (context.inline) {3806 isStatic = false;3807 }3808 }3809 // skip :is on <component>3810 if (name === 'is' && isComponentTag(tag)) {3811 continue;3812 }3813 properties.push(createObjectProperty(createSimpleExpression(name, true, getInnerRange(loc, 0, name.length)), createSimpleExpression(value ? value.content : '', isStatic, value ? value.loc : loc)));3814 }3815 else {3816 // directives3817 const { name, arg, exp, loc } = prop;3818 const isBind = name === 'bind';3819 const isOn = name === 'on';3820 // skip v-slot - it is handled by its dedicated transform.3821 if (name === 'slot') {3822 if (!isComponent) {3823 context.onError(createCompilerError(39 /* X_V_SLOT_MISPLACED */, loc));3824 }3825 continue;3826 }3827 // skip v-once - it is handled by its dedicated transform.3828 if (name === 'once') {3829 continue;3830 }3831 // skip v-is and :is on <component>3832 if (name === 'is' ||3833 (isBind && isComponentTag(tag) && isBindKey(arg, 'is'))) {3834 continue;3835 }3836 // skip v-on in SSR compilation3837 if (isOn && ssr) {3838 continue;3839 }3840 // special case for v-bind and v-on with no argument3841 if (!arg && (isBind || isOn)) {3842 hasDynamicKeys = true;3843 if (exp) {3844 if (properties.length) {3845 mergeArgs.push(createObjectExpression(dedupeProperties(properties), elementLoc));3846 properties = [];3847 }3848 if (isBind) {3849 mergeArgs.push(exp);3850 }3851 else {3852 // v-on="obj" -> toHandlers(obj)3853 mergeArgs.push({3854 type: 14 /* JS_CALL_EXPRESSION */,3855 loc,3856 callee: context.helper(TO_HANDLERS),3857 arguments: [exp]3858 });3859 }3860 }3861 else {3862 context.onError(createCompilerError(isBind3863 ? 33 /* X_V_BIND_NO_EXPRESSION */3864 : 34 /* X_V_ON_NO_EXPRESSION */, loc));3865 }3866 continue;3867 }3868 const directiveTransform = context.directiveTransforms[name];3869 if (directiveTransform) {3870 // has built-in directive transform.3871 const { props, needRuntime } = directiveTransform(prop, node, context);3872 !ssr && props.forEach(analyzePatchFlag);3873 properties.push(...props);3874 if (needRuntime) {3875 runtimeDirectives.push(prop);3876 if (shared.isSymbol(needRuntime)) {3877 directiveImportMap.set(prop, needRuntime);3878 }3879 }3880 }3881 else {3882 // no built-in transform, this is a user custom directive.3883 runtimeDirectives.push(prop);3884 }3885 }3886 }3887 let propsExpression = undefined;3888 // has v-bind="object" or v-on="object", wrap with mergeProps3889 if (mergeArgs.length) {3890 if (properties.length) {3891 mergeArgs.push(createObjectExpression(dedupeProperties(properties), elementLoc));3892 }3893 if (mergeArgs.length > 1) {3894 propsExpression = createCallExpression(context.helper(MERGE_PROPS), mergeArgs, elementLoc);3895 }3896 else {3897 // single v-bind with nothing else - no need for a mergeProps call3898 propsExpression = mergeArgs[0];3899 }3900 }3901 else if (properties.length) {3902 propsExpression = createObjectExpression(dedupeProperties(properties), elementLoc);3903 }3904 // patchFlag analysis3905 if (hasDynamicKeys) {3906 patchFlag |= 16 /* FULL_PROPS */;3907 }3908 else {3909 if (hasClassBinding) {3910 patchFlag |= 2 /* CLASS */;3911 }3912 if (hasStyleBinding) {3913 patchFlag |= 4 /* STYLE */;3914 }3915 if (dynamicPropNames.length) {3916 patchFlag |= 8 /* PROPS */;3917 }3918 if (hasHydrationEventBinding) {3919 patchFlag |= 32 /* HYDRATE_EVENTS */;3920 }3921 }3922 if ((patchFlag === 0 || patchFlag === 32 /* HYDRATE_EVENTS */) &&3923 (hasRef || hasVnodeHook || runtimeDirectives.length > 0)) {3924 patchFlag |= 512 /* NEED_PATCH */;3925 }3926 return {3927 props: propsExpression,3928 directives: runtimeDirectives,3929 patchFlag,3930 dynamicPropNames3931 };3932}3933// Dedupe props in an object literal.3934// Literal duplicated attributes would have been warned during the parse phase,3935// however, it's possible to encounter duplicated `onXXX` handlers with different3936// modifiers. We also need to merge static and dynamic class / style attributes.3937// - onXXX handlers / style: merge into array3938// - class: merge into single expression with concatenation3939function dedupeProperties(properties) {3940 const knownProps = new Map();3941 const deduped = [];3942 for (let i = 0; i < properties.length; i++) {3943 const prop = properties[i];3944 // dynamic keys are always allowed3945 if (prop.key.type === 8 /* COMPOUND_EXPRESSION */ || !prop.key.isStatic) {3946 deduped.push(prop);3947 continue;3948 }3949 const name = prop.key.content;3950 const existing = knownProps.get(name);3951 if (existing) {3952 if (name === 'style' || name === 'class' || name.startsWith('on')) {3953 mergeAsArray(existing, prop);3954 }3955 // unexpected duplicate, should have emitted error during parse3956 }3957 else {3958 knownProps.set(name, prop);3959 deduped.push(prop);3960 }3961 }3962 return deduped;3963}3964function mergeAsArray(existing, incoming) {3965 if (existing.value.type === 17 /* JS_ARRAY_EXPRESSION */) {3966 existing.value.elements.push(incoming.value);3967 }3968 else {3969 existing.value = createArrayExpression([existing.value, incoming.value], existing.loc);3970 }3971}3972function buildDirectiveArgs(dir, context) {3973 const dirArgs = [];3974 const runtime = directiveImportMap.get(dir);3975 if (runtime) {3976 // built-in directive with runtime3977 dirArgs.push(context.helperString(runtime));3978 }3979 else {3980 // user directive.3981 // see if we have directives exposed via <script setup>3982 const fromSetup = resolveSetupReference(dir.name, context);3983 if (fromSetup) {3984 dirArgs.push(fromSetup);3985 }3986 else {3987 // inject statement for resolving directive3988 context.helper(RESOLVE_DIRECTIVE);3989 context.directives.add(dir.name);3990 dirArgs.push(toValidAssetId(dir.name, `directive`));3991 }3992 }3993 const { loc } = dir;3994 if (dir.exp)3995 dirArgs.push(dir.exp);3996 if (dir.arg) {3997 if (!dir.exp) {3998 dirArgs.push(`void 0`);3999 }4000 dirArgs.push(dir.arg);4001 }4002 if (Object.keys(dir.modifiers).length) {4003 if (!dir.arg) {4004 if (!dir.exp) {4005 dirArgs.push(`void 0`);4006 }4007 dirArgs.push(`void 0`);4008 }4009 const trueExpression = createSimpleExpression(`true`, false, loc);4010 dirArgs.push(createObjectExpression(dir.modifiers.map(modifier => createObjectProperty(modifier, trueExpression)), loc));4011 }4012 return createArrayExpression(dirArgs, dir.loc);4013}4014function stringifyDynamicPropNames(props) {4015 let propsNamesString = `[`;4016 for (let i = 0, l = props.length; i < l; i++) {4017 propsNamesString += JSON.stringify(props[i]);4018 if (i < l - 1)4019 propsNamesString += ', ';4020 }4021 return propsNamesString + `]`;4022}4023function isComponentTag(tag) {4024 return tag[0].toLowerCase() + tag.slice(1) === 'component';4025}4026const cacheStringFunction = (fn) => {4027 const cache = Object.create(null);4028 return ((str) => {4029 const hit = cache[str];4030 return hit || (cache[str] = fn(str));4031 });4032};4033const camelizeRE = /-(\w)/g;4034/**4035 * @private4036 */4037const camelize = cacheStringFunction((str) => {
...
compiler-core.esm-bundler.js
Source:compiler-core.esm-bundler.js
...3217};3218function resolveComponentType(node, context, ssr = false) {3219 const { tag } = node;3220 // 1. dynamic component3221 const isProp = isComponentTag(tag)3222 ? findProp(node, 'is')3223 : findDir(node, 'is');3224 if (isProp) {3225 const exp = isProp.type === 6 /* ATTRIBUTE */3226 ? isProp.value && createSimpleExpression(isProp.value.content, true)3227 : isProp.exp;3228 if (exp) {3229 return createCallExpression(context.helper(RESOLVE_DYNAMIC_COMPONENT), [3230 exp3231 ]);3232 }3233 }3234 // 2. built-in components (Teleport, Transition, KeepAlive, Suspense...)3235 const builtIn = isCoreComponent(tag) || context.isBuiltInComponent(tag);3236 if (builtIn) {3237 // built-ins are simply fallthroughs / have special handling during ssr3238 // so we don't need to import their runtime equivalents3239 if (!ssr)3240 context.helper(builtIn);3241 return builtIn;3242 }3243 // 5. user component (resolve)3244 context.helper(RESOLVE_COMPONENT);3245 context.components.add(tag);3246 return toValidAssetId(tag, `component`);3247}3248function buildProps(node, context, props = node.props, ssr = false) {3249 const { tag, loc: elementLoc } = node;3250 const isComponent = node.tagType === 1 /* COMPONENT */;3251 let properties = [];3252 const mergeArgs = [];3253 const runtimeDirectives = [];3254 // patchFlag analysis3255 let patchFlag = 0;3256 let hasRef = false;3257 let hasClassBinding = false;3258 let hasStyleBinding = false;3259 let hasHydrationEventBinding = false;3260 let hasDynamicKeys = false;3261 let hasVnodeHook = false;3262 const dynamicPropNames = [];3263 const analyzePatchFlag = ({ key, value }) => {3264 if (isStaticExp(key)) {3265 const name = key.content;3266 const isEventHandler = isOn(name);3267 if (!isComponent &&3268 isEventHandler &&3269 // omit the flag for click handlers because hydration gives click3270 // dedicated fast path.3271 name.toLowerCase() !== 'onclick' &&3272 // omit v-model handlers3273 name !== 'onUpdate:modelValue' &&3274 // omit onVnodeXXX hooks3275 !isReservedProp(name)) {3276 hasHydrationEventBinding = true;3277 }3278 if (isEventHandler && isReservedProp(name)) {3279 hasVnodeHook = true;3280 }3281 if (value.type === 20 /* JS_CACHE_EXPRESSION */ ||3282 ((value.type === 4 /* SIMPLE_EXPRESSION */ ||3283 value.type === 8 /* COMPOUND_EXPRESSION */) &&3284 getConstantType(value, context) > 0)) {3285 // skip if the prop is a cached handler or has constant value3286 return;3287 }3288 if (name === 'ref') {3289 hasRef = true;3290 }3291 else if (name === 'class' && !isComponent) {3292 hasClassBinding = true;3293 }3294 else if (name === 'style' && !isComponent) {3295 hasStyleBinding = true;3296 }3297 else if (name !== 'key' && !dynamicPropNames.includes(name)) {3298 dynamicPropNames.push(name);3299 }3300 }3301 else {3302 hasDynamicKeys = true;3303 }3304 };3305 for (let i = 0; i < props.length; i++) {3306 // static attribute3307 const prop = props[i];3308 if (prop.type === 6 /* ATTRIBUTE */) {3309 const { loc, name, value } = prop;3310 let isStatic = true;3311 if (name === 'ref') {3312 hasRef = true;3313 }3314 // skip :is on <component>3315 if (name === 'is' && isComponentTag(tag)) {3316 continue;3317 }3318 properties.push(createObjectProperty(createSimpleExpression(name, true, getInnerRange(loc, 0, name.length)), createSimpleExpression(value ? value.content : '', isStatic, value ? value.loc : loc)));3319 }3320 else {3321 // directives3322 const { name, arg, exp, loc } = prop;3323 const isBind = name === 'bind';3324 const isOn = name === 'on';3325 // skip v-slot - it is handled by its dedicated transform.3326 if (name === 'slot') {3327 if (!isComponent) {3328 context.onError(createCompilerError(39 /* X_V_SLOT_MISPLACED */, loc));3329 }3330 continue;3331 }3332 // skip v-once - it is handled by its dedicated transform.3333 if (name === 'once') {3334 continue;3335 }3336 // skip v-is and :is on <component>3337 if (name === 'is' ||3338 (isBind && isComponentTag(tag) && isBindKey(arg, 'is'))) {3339 continue;3340 }3341 // skip v-on in SSR compilation3342 if (isOn && ssr) {3343 continue;3344 }3345 // special case for v-bind and v-on with no argument3346 if (!arg && (isBind || isOn)) {3347 hasDynamicKeys = true;3348 if (exp) {3349 if (properties.length) {3350 mergeArgs.push(createObjectExpression(dedupeProperties(properties), elementLoc));3351 properties = [];3352 }3353 if (isBind) {3354 mergeArgs.push(exp);3355 }3356 else {3357 // v-on="obj" -> toHandlers(obj)3358 mergeArgs.push({3359 type: 14 /* JS_CALL_EXPRESSION */,3360 loc,3361 callee: context.helper(TO_HANDLERS),3362 arguments: [exp]3363 });3364 }3365 }3366 else {3367 context.onError(createCompilerError(isBind3368 ? 33 /* X_V_BIND_NO_EXPRESSION */3369 : 34 /* X_V_ON_NO_EXPRESSION */, loc));3370 }3371 continue;3372 }3373 const directiveTransform = context.directiveTransforms[name];3374 if (directiveTransform) {3375 // has built-in directive transform.3376 const { props, needRuntime } = directiveTransform(prop, node, context);3377 !ssr && props.forEach(analyzePatchFlag);3378 properties.push(...props);3379 if (needRuntime) {3380 runtimeDirectives.push(prop);3381 if (isSymbol(needRuntime)) {3382 directiveImportMap.set(prop, needRuntime);3383 }3384 }3385 }3386 else {3387 // no built-in transform, this is a user custom directive.3388 runtimeDirectives.push(prop);3389 }3390 }3391 }3392 let propsExpression = undefined;3393 // has v-bind="object" or v-on="object", wrap with mergeProps3394 if (mergeArgs.length) {3395 if (properties.length) {3396 mergeArgs.push(createObjectExpression(dedupeProperties(properties), elementLoc));3397 }3398 if (mergeArgs.length > 1) {3399 propsExpression = createCallExpression(context.helper(MERGE_PROPS), mergeArgs, elementLoc);3400 }3401 else {3402 // single v-bind with nothing else - no need for a mergeProps call3403 propsExpression = mergeArgs[0];3404 }3405 }3406 else if (properties.length) {3407 propsExpression = createObjectExpression(dedupeProperties(properties), elementLoc);3408 }3409 // patchFlag analysis3410 if (hasDynamicKeys) {3411 patchFlag |= 16 /* FULL_PROPS */;3412 }3413 else {3414 if (hasClassBinding) {3415 patchFlag |= 2 /* CLASS */;3416 }3417 if (hasStyleBinding) {3418 patchFlag |= 4 /* STYLE */;3419 }3420 if (dynamicPropNames.length) {3421 patchFlag |= 8 /* PROPS */;3422 }3423 if (hasHydrationEventBinding) {3424 patchFlag |= 32 /* HYDRATE_EVENTS */;3425 }3426 }3427 if ((patchFlag === 0 || patchFlag === 32 /* HYDRATE_EVENTS */) &&3428 (hasRef || hasVnodeHook || runtimeDirectives.length > 0)) {3429 patchFlag |= 512 /* NEED_PATCH */;3430 }3431 return {3432 props: propsExpression,3433 directives: runtimeDirectives,3434 patchFlag,3435 dynamicPropNames3436 };3437}3438// Dedupe props in an object literal.3439// Literal duplicated attributes would have been warned during the parse phase,3440// however, it's possible to encounter duplicated `onXXX` handlers with different3441// modifiers. We also need to merge static and dynamic class / style attributes.3442// - onXXX handlers / style: merge into array3443// - class: merge into single expression with concatenation3444function dedupeProperties(properties) {3445 const knownProps = new Map();3446 const deduped = [];3447 for (let i = 0; i < properties.length; i++) {3448 const prop = properties[i];3449 // dynamic keys are always allowed3450 if (prop.key.type === 8 /* COMPOUND_EXPRESSION */ || !prop.key.isStatic) {3451 deduped.push(prop);3452 continue;3453 }3454 const name = prop.key.content;3455 const existing = knownProps.get(name);3456 if (existing) {3457 if (name === 'style' || name === 'class' || name.startsWith('on')) {3458 mergeAsArray(existing, prop);3459 }3460 // unexpected duplicate, should have emitted error during parse3461 }3462 else {3463 knownProps.set(name, prop);3464 deduped.push(prop);3465 }3466 }3467 return deduped;3468}3469function mergeAsArray(existing, incoming) {3470 if (existing.value.type === 17 /* JS_ARRAY_EXPRESSION */) {3471 existing.value.elements.push(incoming.value);3472 }3473 else {3474 existing.value = createArrayExpression([existing.value, incoming.value], existing.loc);3475 }3476}3477function buildDirectiveArgs(dir, context) {3478 const dirArgs = [];3479 const runtime = directiveImportMap.get(dir);3480 if (runtime) {3481 // built-in directive with runtime3482 dirArgs.push(context.helperString(runtime));3483 }3484 else {3485 {3486 // inject statement for resolving directive3487 context.helper(RESOLVE_DIRECTIVE);3488 context.directives.add(dir.name);3489 dirArgs.push(toValidAssetId(dir.name, `directive`));3490 }3491 }3492 const { loc } = dir;3493 if (dir.exp)3494 dirArgs.push(dir.exp);3495 if (dir.arg) {3496 if (!dir.exp) {3497 dirArgs.push(`void 0`);3498 }3499 dirArgs.push(dir.arg);3500 }3501 if (Object.keys(dir.modifiers).length) {3502 if (!dir.arg) {3503 if (!dir.exp) {3504 dirArgs.push(`void 0`);3505 }3506 dirArgs.push(`void 0`);3507 }3508 const trueExpression = createSimpleExpression(`true`, false, loc);3509 dirArgs.push(createObjectExpression(dir.modifiers.map(modifier => createObjectProperty(modifier, trueExpression)), loc));3510 }3511 return createArrayExpression(dirArgs, dir.loc);3512}3513function stringifyDynamicPropNames(props) {3514 let propsNamesString = `[`;3515 for (let i = 0, l = props.length; i < l; i++) {3516 propsNamesString += JSON.stringify(props[i]);3517 if (i < l - 1)3518 propsNamesString += ', ';3519 }3520 return propsNamesString + `]`;3521}3522function isComponentTag(tag) {3523 return tag[0].toLowerCase() + tag.slice(1) === 'component';3524}35253526(process.env.NODE_ENV !== 'production')3527 ? Object.freeze({})3528 : {};3529(process.env.NODE_ENV !== 'production') ? Object.freeze([]) : [];3530const cacheStringFunction = (fn) => {3531 const cache = Object.create(null);3532 return ((str) => {3533 const hit = cache[str];3534 return hit || (cache[str] = fn(str));3535 });3536};
...
compiler.js
Source:compiler.js
...251const componentEvaluator = (code) => {252 let results = "";253 parser.HTMLParser(code, {254 start: (tag, attrs, unary) => {255 if (isComponentTag(tag)) {256 attrs.push({257 name: "__trex_module",258 value: true,259 }, {260 name: constants.trexComponentAttributeKey,261 value: tag262 });263 results += `<${constants.trexModuleName}`;264 }265 else {266 results += `<${tag}`;267 }268 results += attrs269 .reduce((t, { name, value }, _, arr) => {270 return `${t} ${name}="${value}"`;271 }, "");272 results += (unary ? "/" : "") + ">";273 },274 end: tag => {275 if (isComponentTag(tag)) {276 results += `</${constants.trexModuleName}>`;277 }278 else {279 results += `</${tag}>`;280 }281 },282 chars: text => {283 results += text284 .replace(/\{\{/g, "<trex-interpolation><textarea hidden>")285 .replace(/\}\}/g, "</textarea></trex-interpolation>");286 },287 comment: text => {288 results += `<!--${text}-->`;289 },...
compiler.mjs
Source:compiler.mjs
...133 const [tag, rawTag, isDynamicTag] = parseValue(vnode.tag);134 if (vnode.ref === "this") {135 generateProperties(vnode, $);136 generateChildren(vnode, $);137 } else if (!isDynamicTag && !isComponentTag(rawTag)) {138 $.html += `<${rawTag}`;139 generateProperties(vnode, $);140 $.html += ">";141 if (vnode.void) return;142 generateChildren(vnode, $);143 $.html += `</${rawTag}>`;144 } else if (isComponentTag(rawTag)) {145 const [splats, attrs, kvs] = Object.entries(vnode.properties).reduce(([splats, attrs, kvs], [k, v]) => {146 if (splatRegexp.test(k)) return [splats.concat(k.match(splatRegexp)[1]), attrs, kvs];147 else if (k === "id" || k === "class") return [splats, attrs.concat(`${k}="${v}"`), kvs];148 return [splats, attrs, kvs.concat(`[${parseValue(k)[0]}]: ${parseValue(v)[0]}`)];149 }, [[], [], []]);150 $.html += `<${rawTag}${attrs.length ? " " + attrs.join(" ") : ""}>`;151 generateChildren(vnode, $);152 $.html += `</${rawTag}>`;153 const props = splats.length ? `Object.assign({}, ${splats.join(", ")}, {${kvs.join(", ")}})` : `{${kvs.join(", ")}}`;154 $.create += `${vnode.ref}.init($.app, $, ${props});\n`;155 $.update += `${vnode.ref}.update(${props});\n`;156 } else {157 throw new Error("dynamic tags are currently not supported");158 }159}160function generateProperties(vnode, $) {161 const dynamicProperties = [];162 for (const k in vnode.properties) {163 const [key, rawKey, isDynamicKey] = parseValue(k),164 [value, rawValue, isDynamicValue] = parseValue(vnode.properties[k]);165 if (splatRegexp.test(k)) throw new Error(`splat properties are only allowed on component tags, not <${vnode.tag}>`);166 else if (!isDynamicKey && !isDynamicValue && vnode.ref === "this") $.create += `xm.setTemplateProperty(${vnode.ref}, ${key}, ${value});\n`;167 else if (!isDynamicKey && !isDynamicValue) $.html += ` ${rawKey}=${value}`;168 else dynamicProperties.push({key, value, isDynamicKey, isDynamicValue});169 }170 if (!dynamicProperties.length) return;171 const node = generateLocalNodeRef($, vnode, "properties");172 for (let {key, value, isDynamicKey, isDynamicValue} of dynamicProperties) {173 if (!isDynamicKey) {174 $.create += `xm.setProperty(${node}, ${key}, ${value});\n`;175 $.update += `xm.setProperty(${node}, ${key}, ${value});\n`;176 } else {177 const _ = prefix("properties");178 $.create += `let ${_}key = ${key}; xm.setProperty(${node}, ${_}key, ${value});\n`;179 $.update += `${_}key = xm.setDynamicKeyProperty(${node}, ${_}key, ${key}, ${value});\n`;180 }181 }182}183function generateChildren(vnode, $) {184 let node = vnode.ref + ".firstChild", dynamicChildren = [];185 for (const vchild of vnode.children) {186 node = generateNodeRef($, {ref: node}, "children");187 if (vchild.tag) {188 vchild.ref = node;189 generateVnode(vchild, $);190 node = vchild.ref;191 node = node + ".nextSibling";192 } else {193 for (let [value, rawValue, isDynamic] of parseValueParts(vchild)[0]) {194 $.html += isDynamic ? "<!---->" : rawValue;195 if (isDynamic) dynamicChildren.push([node, value]);196 node = node + ".nextSibling";197 }198 }199 }200 if (dynamicChildren.length) {201 const _ = prefix("children"), values = dynamicChildren.map(([_, v]) => v), nodes = dynamicChildren.map(([n]) => n),202 node = generateLocalNodeRef($, vnode, "children");203 $.create += `const ${_}nodes = [${nodes}], ${_}values = [];204 xm.updateChildNodes(${node}, null, ${_}nodes, ${_}values, [${values}], $, xm.createChildNode);\n`;205 $.update += `xm.updateChildNodes(${node}, null, ${_}nodes, ${_}values, [${values}], $, xm.createChildNode);\n`;206 }207}208function isComponentTag(tag) {209 return tag && tag.startsWith("x-");210}211function generateNodeRef($, vnode, key = "") {212 if (vnode.ref.indexOf(".") === -1) return vnode.ref;213 return vnode.ref = generateLocalNodeRef($, vnode, key);214}215function generateLocalNodeRef($, vnode, key = "") {216 const _ = prefix(key);217 $.create += `let ${_}node = ${vnode.ref};\n`;218 return `${_}node`;219}220let prefixId = 0;221export const resetPrefixId = () => prefixId = 0;222const prefix = (key) => `_${key}_${prefixId++}_`;
layout-element-parser.js
Source:layout-element-parser.js
...19 */20const isProp = element => !!element && typeof element === 'object' && !Array.isArray(element);21const isComponentTag = layoutElement => Array.isArray(layoutElement) && typeof layoutElement[0] === 'string' && layoutElement[0].startsWith('@') && isProp(layoutElement[1]);22const hasOnlyStringsProperties = function (layoutElement) {23 return Array.isArray(layoutElement) && layoutElement.length > 0 && !isComponentTag(layoutElement) && !layoutElement.find(el => typeof el !== 'string');24};25const hasArrayOfLayoutElements = function (layoutElement) {26 return Array.isArray(layoutElement) && layoutElement.length > 0 && !layoutElement.find(element => !Array.isArray(element));27};28const hasFirstStringProperty = function (layoutElement) {29 return Array.isArray(layoutElement) && typeof layoutElement[0] === 'string' && !isComponentTag(layoutElement);30};31const getPropertyNames = layoutElement => {32 if (typeof layoutElement === 'string') {33 return [layoutElement];34 }35 if (hasOnlyStringsProperties(layoutElement)) {36 return layoutElement;37 }38 if (hasFirstStringProperty(layoutElement)) {39 return [layoutElement[0]];40 }41 return [];42};43const getInnerLayoutElements = layoutElement => {44 // only cases like [{}, layoutElement] (whatever follows props)45 if (Array.isArray(layoutElement) && isProp(layoutElement[0])) {46 return layoutElement[1];47 }48 if (hasArrayOfLayoutElements(layoutElement)) {49 return layoutElement;50 }51 return [];52};53const getComponent = layoutElement => {54 if (isComponentTag(layoutElement)) {55 return layoutElement[0].slice(1);56 }57 return 'Box';58};59const getProps = layoutElement => {60 if (Array.isArray(layoutElement) && layoutElement.length) {61 const boxProps = layoutElement.find(isProp);62 return boxProps || {};63 }64 return {};65};66const layoutElementParser = layoutElement => {67 const props = getProps(layoutElement);68 const innerLayoutElements = getInnerLayoutElements(layoutElement);...
props.js
Source:props.js
1const path = require('path')2const fs = require('fs-extra')3const Config = require('../config.js')4module.exports = function(ast, fileInfo, renderAxml) {5 let isComponentTag = false6 const { type, props } = ast7 if (props) {8 Object.keys(props).forEach((key) => {9 if (key && !props[key].value[0]) {10 props[key] = { type: 'double', value: [' '] }11 }12 })13 }14 15 /**16 * èªå®ä¹ç»ä»¶é¢å¤ç - äºä»¶17 */18 isComponentTag = processCustomComponent(ast, fileInfo)19 return isComponentTag20}21function checkoutCustomComponent(fileInfo, tagName) {22 let bool = false; let json; let23 appJson24 if (fileInfo.extname === '.axml') {25 json = fileInfo.path.replace('.axml', '.json')26 if (!fs.pathExistsSync(json)) { return false }27 if (!fileInfo.jsonUsingComponents) {28 json = JSON.parse(fs.readFileSync(json, 'utf8')) || {}29 } else {30 json = fileInfo.jsonUsingComponents31 }32 if (json.usingComponents && json.usingComponents[tagName]) {33 bool = true34 }35 36 if (!tagName) {37 fileInfo.jsonUsingComponents = fileInfo.jsonUsingComponents || json.usingComponents38 return {39 component: json.usingComponents,40 }41 }42 }43 return bool44}45function processCustomComponent(ast, fileInfo) {46 let isComponentTag = false47 /**48 * èªå®ä¹ç»ä»¶äºä»¶å¤ç49 */50 51 if (!fileInfo.jsonUsingComponents) {52 const customComponents = checkoutCustomComponent(fileInfo) || {}53 fileInfo.jsonUsingComponents = customComponents.component || {}54 }55 if (fileInfo.jsonUsingComponents[ast.type]) {56 isComponentTag = true57 if (ast.props && !Config.component2) {58 ast.props._parent_ref = { type: 'double', value: ['{{isMounted}}'] }59 }60 }61 return isComponentTag...
Using AI Code Generation
1const { isComponentTag } = require('@playwright/test/lib/api/test/page');2const { chromium } = require('playwright');3(async () => {4 const browser = await chromium.launch();5 const context = await browser.newContext();6 const page = await context.newPage();7 const selector = 'text=Get Started';8 const isComponent = await isComponentTag(page, selector);9 console.log(isComponent);10 await browser.close();11})();
Using AI Code Generation
1const { isComponentTag } = require('@playwright/test/lib/internal/locators');2const { test } = require('@playwright/test');3test('test', async ({ page }) => {4 await page.click(isComponentTag('a'));5 await page.click(isComponentTag('button'));6});7isComponentTag(tagName)8const { isComponentTag } = require('@playwright/test/lib/internal/locators');9const { test } = require('@playwright/test');10test('test', async ({ page }) => {11 await page.click(isComponentTag('a'));12 await page.click(isComponentTag('button'));13});14isComponentText(text)15const { isComponentText } = require('@playwright/test/lib/internal/locators');16const { test } = require('@playwright/test');17test('test', async ({ page }) => {18 await page.click(isComponentText('Docs'));19 await page.click(isComponentText('Blog'));20});21isComponentAttribute(name, value)22const { isComponentAttribute } = require('@playwright/test/lib/internal/locators');23const { test } = require('@playwright/test');24test('test', async ({ page }) => {25 await page.click(isComponentAttribute('href', '/docs'));26 await page.click(isComponentAttribute('href', '/blog'));27});28isComponentDataTest(name)29const { isComponentDataTest } = require('@playwright/test/lib/internal/locators');30const { test } = require('@playwright/test');31test('test', async ({ page }) => {32 await page.click(isComponentDataTest('docs-link'));33 await page.click(isComponentDataTest('blog-link'));34});
Using AI Code Generation
1const { isComponentTag } = require('playwright/lib/server/supplements/utils/componentUtils');2const { Page } = require('playwright/lib/server/page');3const { ElementHandle } = require('playwright/lib/server/dom');4const { PageBinding } = require('playwright/lib/server/pageBinding');5const { JSHandle } = require('playwright/lib/server/jsHandle');6const { test } = require('@playwright/test');7test('test', async ({ page }) => {8 await page.setContent('<div></div>');9 const div = await page.$('div');10 const component = await page.evaluateHandle((div) => {11 return div.attachShadow({ mode: 'open' });12 }, div);13 await component.evaluate(() => {14 const div = document.createElement('div');15 div.innerHTML = 'Hello';16 document.body.appendChild(div);17 });18 const divHandle = await component.$('div');19 const isComponent = await isComponentTag(divHandle);20 console.log(isComponent);21});
Using AI Code Generation
1const { isComponentTag } = require('@playwright/test');2const { isComponentTag } = require('@playwright/test');3const { isComponentTag } = require('@playwright/test');4const { isComponentTag } = require('@playwright/test');5const { isComponentTag } = require('@playwright/test');6const { isComponentTag } = require('@playwright/test');
Using AI Code Generation
1const { isComponentTag } = require('@playwright/test/lib/server/frames');2const { test } = require('@playwright/test');3test('is component tag', async ({ page }) => {4 await page.setContent('<div></div>');5 const div = await page.$('div');6 const divHandle = await div.asElement();7 const isComponent = await page.evaluate((handle) => {8 return isComponentTag(handle);9 }, divHandle);10 console.log(isComponent);11});12const { isComponentTag } = require('@playwright/test/lib/server/frames');13const { test } = require('@playwright/test');14test('is component tag', async ({ page }) => {15 await page.setContent('<div></div>');16 const div = await page.$('div');17 const divHandle = await div.asElement();18 const isComponent = await page.evaluate((handle) => {19 return isComponentTag(handle);20 }, divHandle);21 expect(isComponent).toBe(true);22});23 FAIL test.spec.js (6.746 s)24 14 | const divHandle = await div.asElement();25 15 | const isComponent = await page.evaluate((handle) => {26 > 16 | return isComponentTag(handle);27 17 | }, divHandle);28 18 | console.log(isComponent);29 19 | });30 at ExecutionContext._evaluateInternal (node_modules/playwright
Using AI Code Generation
1const { isComponentTag } = require('@playwright/test');2const { test, expect } = require('@playwright/test');3test('Check if the given tag is a component tag or not', async ({ page }) => {4 expect(isComponentTag('input')).toBe(true);5 expect(isComponentTag('div')).toBe(false);6});7const { test, expect } = require('@playwright/test');8test('Check if the given tag is a component tag or not', async ({ page }) => {9 expect(isComponentTag('input')).toBe(true);10 expect(isComponentTag('div')).toBe(false);11});12Types of Test Cases in Software Testing | Set 1 (Functional and Non-Functional)
LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.
Get 100 minutes of automation test minutes FREE!!