Compare commits

..

225 Commits

Author SHA1 Message Date
Steve Van Essche 6891ad2773 Update component.md
Replace `onWillUnmount` by `onWillDestroy` in the said paragraph
2025-03-14 08:47:30 -04:00
Géry Debongnie a9f29c4caa [FIX] refs in recursive templates now work properly 2022-02-09 10:26:23 +01:00
Géry Debongnie 74faa8bb82 [MISC] tools: fix version string 2022-02-08 13:44:18 +01:00
Bruno Boi 5940048a19 [FIX] can use t-out with String classes 2022-02-08 13:43:02 +01:00
Géry Debongnie 554157637b [FIX] slots: process slot params/values like normal props 2022-02-08 10:58:49 +01:00
Géry Debongnie 3c2f073c11 [DOC] improve changelog 2022-02-07 14:58:02 +01:00
Géry Debongnie f095f9e5aa [IMP] blockdom: apply dynamic part of block before inserting it 2022-02-04 13:55:44 +01:00
Géry Debongnie a402115227 [IMP] props validation: cannot set default value on mandatory props 2022-02-04 13:55:44 +01:00
Géry Debongnie 2afa1f4ace [DOC] add some info to the changelog 2022-02-04 13:55:44 +01:00
Géry Debongnie 5a7928d3e4 [IMP] compiler: translatableAttributes can be added/removed 2022-02-03 13:41:49 +01:00
Samuel Degueldre 9424312573 [IMP] compiler: allow to declare default slot scope on component 2022-02-02 09:09:23 +01:00
Géry Debongnie c5e197ddc1 [REF] allow external code to override validateTarget 2022-02-02 08:51:35 +01:00
Géry Debongnie fa7a393f27 [IMP] component: validate mounting target at patch time 2022-01-31 17:01:20 +01:00
Géry Debongnie efaa186544 [IMP] component: disallow calling hooks outside of setup
(and constructor)

Doing so could cause strange and difficult bugs
2022-01-31 15:08:31 +01:00
Bruno Boi 45ea6a195e [FIX] hooks: useSubEnv will not erase previous useChildSubEnv 2022-01-31 14:49:25 +01:00
Géry Debongnie 2ce0c39674 [IMP] app: introduce test mode
Same as `dev` mode, but without warning in console
2022-01-31 08:57:08 +01:00
Géry Debongnie 924dffeb36 [TEST] component: add test to make sure a specific issue does not arise 2022-01-27 15:02:44 +01:00
Géry Debongnie 0dbc807e01 [IMP] typing: make app and mount method properly generic 2022-01-27 14:43:33 +01:00
Bruno Boi 4b88787a72 [FIX] compiler: never add _ prefix to non variable token 2022-01-27 14:41:16 +01:00
Samuel Degueldre 6f0a4b1c92 [FIX] compiler: do not pass dynamic props object as is
The child receiving the props can observe changes made to the passed
t-props object which is not desirable.
2022-01-27 13:19:14 +01:00
Bruno Boi 3f575148b6 [FIX] compiler: add _ prefix to local variables while compiling an expression 2022-01-27 10:57:13 +01:00
Géry Debongnie 4a971f2963 [IMP] hooks: introduce useChildSubEnv and change useSubEnv 2022-01-27 10:35:06 +01:00
Géry Debongnie d7850aaf7a [FIX] compiler: does not modify xml doc in place 2022-01-27 10:34:41 +01:00
Bruno Boi 0e13b859d0 [FIX] component: properly capture expression of t-model 2022-01-27 09:23:52 +01:00
Géry Debongnie c813a1ce03 [DOC] fix error in slot documentation 2022-01-25 12:37:07 +01:00
Géry Debongnie 54d69c2dde [FIX] reactivity: clear callbacks at destroy time instead of unmount 2022-01-24 16:36:32 +01:00
Géry Debongnie 11d4aae8d2 [FIX] compiler: add missing ; in some places 2022-01-24 14:51:12 +01:00
Géry Debongnie b159a073d9 [FIX] component: proper error message in dev mode in some cases 2022-01-24 13:18:19 +01:00
Lucas Perais (lpe) d9b189bcba [FIX] compiler: force new block for svg nested in html 2022-01-24 12:56:43 +01:00
Géry Debongnie 7ef1fe0b99 [FIX] svg: allow path as root tag 2022-01-24 10:29:22 +01:00
Samuel Degueldre 211ecdf689 [FIX] blockdom: fix VHtml patching not setting its html correctly 2022-01-24 09:36:43 +01:00
Lucas Perais (lpe) b208894d38 [FIX] compiler: t-model on select with options with dynamic values 2022-01-21 16:38:58 +01:00
Géry Debongnie 0737bb39b4 [DOC] update changelog renderToString example 2022-01-21 15:40:27 +01:00
Géry Debongnie de584b01a8 [FIX] reactivity: do not observe eventtarget and other stuff 2022-01-21 15:40:27 +01:00
Géry Debongnie 525029b682 [IMP] reactivity: introduces markRaw and toRaw functions 2022-01-21 15:40:27 +01:00
Lucas Perais (lpe) b88f7a03e9 [FIX] compiler: svg in new block takes the right namespace 2022-01-21 09:32:22 +01:00
Lucas Perais (lpe) 055cbda750 [FIX] compiler: t-key on a sub-domnode pushes an anchor in parent block 2022-01-20 18:13:01 +01:00
Géry Debongnie 45f6a8e97a [REF] utils: move batched from reactivity to utils 2022-01-20 15:48:50 +01:00
Géry Debongnie 6091912c54 [IMP] component: improve error message when invalid handler 2022-01-20 15:48:50 +01:00
Géry Debongnie 586f90f6d1 [FIX] doc: fix broken link, and fix doc link checker test 2022-01-20 12:41:31 +01:00
Géry Debongnie e62a0ca2d9 [DOC] update changelog (add browser removal to changes) 2022-01-20 12:29:51 +01:00
Géry Debongnie 3a4ee160ed [FIX] playground: update window management example 2022-01-20 12:29:51 +01:00
Géry Debongnie 5f24caf40d [FIX] scheduler: make sure raf is bound to window 2022-01-20 11:06:59 +01:00
Géry Debongnie 8e36a11b6e [REF] scheduler: capture requestAnimationFrame asap
This is useful to prevent interactions with other testing code.
2022-01-20 10:19:55 +01:00
Géry Debongnie 9a54afcdf5 [FIX] playground: update example to owl 2 2022-01-19 16:50:08 +01:00
Géry Debongnie 3eefa4b153 [DOC] improve useEffect doc 2022-01-19 16:50:08 +01:00
Géry Debongnie 2ffb94ed98 [FIX] reactivity: export Reactive type 2022-01-19 16:50:08 +01:00
Géry Debongnie 6a15874d23 [FIX] typing: Component class should be generic on Props and Env
Otherwise, it prevents proper typing with typescript
2022-01-19 14:46:26 +01:00
Géry Debongnie 07d34248ae [DOC] update changelog content 2022-01-19 14:45:53 +01:00
Géry Debongnie dfc090bb12 [IMP] portal: ensure that destroy is synchronous 2022-01-19 14:11:59 +01:00
Géry Debongnie 7b3593ff8f [DOC] document synthetic events 2022-01-19 14:00:24 +01:00
Géry Debongnie b06791c950 [IMP] components: crash when using unknown suffix/modifiers 2022-01-19 14:00:24 +01:00
Lucas Perais (lpe) f7843c7c00 [FIX] component, error_handling: do not cancel the error fiber twice 2022-01-19 12:28:28 +01:00
Géry Debongnie ca9d4ea762 [IMP] doc: add changelog to doc test, update changelog 2022-01-19 11:31:53 +01:00
Géry Debongnie 7a407a762d [IMP] ci: fails if circular dependencies are found in build 2022-01-19 10:29:41 +01:00
Géry Debongnie 8847824fb7 [FIX] remove circular dependency 2022-01-19 10:03:30 +01:00
Géry Debongnie 55b068faeb [DOC] remove reference to catchError, fix mistake in changelog 2022-01-19 10:03:30 +01:00
Lucas Perais (lpe) dbee8f50d1 [FIX] components: cascading render after microtaskTick (makeChildFiber)
Co-authored-by: Samuel Degueldre <sad@odoo.com>
Co-authored-by: François Georis <fge@odoo.com>
2022-01-19 09:23:17 +01:00
Géry Debongnie f5fec02d4c [DOC] update changelog 2022-01-19 09:12:26 +01:00
Géry Debongnie 651c53ebbf [DOC] update translations page 2022-01-19 09:12:26 +01:00
Géry Debongnie 3119497508 [DOC] add more information to the slots page 2022-01-19 09:12:26 +01:00
Michael (mcm) 057e6944d6 [FIX] bind lifecycle callbacks to component
Before this commit, some of the callbacks were bound to the component
and some were not.
This commit makes all the callbacks bind to the component.
2022-01-18 15:02:07 +01:00
Géry Debongnie 83d38a6f48 [DOC] reorganize and update documentation to owl 2 2022-01-18 11:07:35 +01:00
Jorge Pinna Puissant 9577d70b4b [IMP] portal: compile t-portal in an internal Component Portal
This commit also clean-up the deepRemove for the Portal that is not
needed any more.
2022-01-17 16:04:01 +01:00
Géry Debongnie 6ff6d59895 [FIX] components: only call handlers if component is mounted 2022-01-17 15:46:18 +01:00
Géry Debongnie 7ba0c9f8ba [FIX] blockdom: toString method in multi could crash 2022-01-17 13:55:39 +01:00
Lucas Perais (lpe) 79a51f2317 [FIX] app: t_call recursive template is bound to the correct this 2022-01-17 10:46:08 +01:00
Lucas Perais (lpe) 483335df6c [FIX] compiler: slot are called with a specific and different key 2022-01-14 15:46:02 +01:00
Samuel Degueldre 287a6d5ac4 [FIX] code_generator: stop matching other variables prefix
Previously, we would replace block declarations in some contexts with
the variable alone, and decalres the variable higher in the generated
code. Issues arise because whe sometimes try to replace "let b2" with
"b2" but end up matching "let b20" which is incorrect.

This commit fixes that by adding a space to the text that we are trying
to match ("let b2 " won't match "let b20")
2022-01-14 09:58:28 +01:00
Lucas Perais (lpe) 68684a9a45 [FIX] component, fiber: subchildren should also patch and destroy their children
Have a GrandParent which controls whether one of its GrandChildren is displayed or not.
First, the GrandChild is displayed. Then, change the state of the GrandParent in order to kill
the GrandChild.

Before this commit the GrandChild is only removed from the DOM, as bdom correctly works.
But it is not destroyed.

After this commit, the GrandChild is correctly destroyed.
2022-01-14 09:51:55 +01:00
Lucas Perais (lpe) e5d773daa8 [FIX] components tests: place tests in right submodule 2022-01-14 09:51:55 +01:00
Jorge Pinna Puissant 00ffab1f64 [IMP] portal: portal as a Directive
Before this commit, portal was a Component, now is a directive.
This commit also clean some unused code, and fix an issue on the clean
optimization when a portal is found in a condition or a loop.
2022-01-13 15:02:11 +01:00
Lucas Perais (lpe) a226f92def [FIX] components: avoid leaks when children are outdated/destroyed
Every use case involving some sort of key set on a component would give birth to a leak in an async context:
- If a key of a component changed, the outdated one was never destroyed.
- destroyed component were never removed from their parent's reference map.

This commit solves both issues, that are tightly linked anyway.
2022-01-13 10:17:47 +01:00
Lucas Perais (lpe) 1749bce155 [FIX] test/helpers: useLogLifeCycle supports custom key 2022-01-13 10:17:47 +01:00
Géry Debongnie c00127fa1e [IMP] tooling: add testTimeout argument to test:debug command 2022-01-13 10:05:10 +01:00
Géry Debongnie 0b4b7899f6 [FIX] portal: properly handle errors
Before this commit, Portal overrode the _render function for its
component node, which means it bypassed the error handling mechanism
that was implemented in that method.  It could have been fixed by
duplicating the error handling code as well, but a better solution in my
opinion is to simply override the renderFn function.  This is closer to
the actual intent of the portal implementation: wrap the result of the
rendering in a VPortal vnode.
2022-01-12 08:51:28 +01:00
Lucas Perais (lpe) 5c324fa075 [FIX] app, components: dynamic t-call should propagate the key
Have a Component which has a Component node, and a dynamic t-call itself having
a Component node.

Before this commit, both children had the same `key`, (as in the key in parent.children, which registers on the parent all its children).

As a result, the scheduler was endlessly hanging.

After this commit, it works as expected.
2022-01-10 15:29:25 +01:00
Géry Debongnie 3945c009ed [REF] compiler: factorize a common pattern 2022-01-10 12:31:37 +01:00
Géry Debongnie 5b1132f01a [FIX] compiler: handle t-set as functions 2021-12-28 13:50:35 +01:00
Géry Debongnie f014b1a33e [DOC] update quick_start and how to test pages 2021-12-22 15:03:38 +01:00
Géry Debongnie 3f09a953b0 [REM] doc: remove overview page 2021-12-22 15:03:38 +01:00
Géry Debongnie b8e7ee5ff7 [DOC] update the tutorial todo app 2021-12-22 15:03:38 +01:00
Géry Debongnie 7fdca35a74 [FIX] reactivity: work even if no callback is given 2021-12-22 14:59:28 +01:00
Géry Debongnie 489e20843c [FIX] components: make sure t-ref work with t-if/t-else 2021-12-22 13:58:02 +01:00
Samuel Degueldre 34b781aeed [IMP] compiler: scope generated ids to their prefix
This means that unrelated ids (eg the id of a template, variable or key)
not longer share the same incrementing counter, meaning that you no
longer see a variable named "v2" unless another variable "v1" was
generated previously, this is also true for block data.
2021-12-22 13:30:09 +01:00
Géry Debongnie e8b0f31da6 [IMP] compiler: improve generated compiled code 2021-12-22 13:30:09 +01:00
Géry Debongnie d160c4a628 [FIX] useEffect: can depend on dom dependencies
Because the dependencies are now computed in patched.
2021-12-22 10:47:28 +01:00
Géry Debongnie 4542171a31 [FIX] portal: do not crash in dev mode
Before this commit, the props validation would fail in dev mode because
it did not expect a slot prop.
2021-12-21 21:43:46 +01:00
Géry Debongnie 0b1c4dd4ef [IMP] app: improve API, small refactoring 2021-12-21 12:12:48 +01:00
Géry Debongnie f4da50d350 [REF] build: do not output const enum definitions
We only use 2 const enums in the codebase, but they are defined in the
output, even though this is not useful in any way. This commit reduces
the final output by about 30 loc.
2021-12-21 09:09:36 +01:00
Géry Debongnie f2921abda8 [REM] component: remove support for css tag 2021-12-21 09:09:36 +01:00
Géry Debongnie 107200fd94 [IMP] components: improve props validation
to be able to specify that additional props are allowed
2021-12-21 09:02:45 +01:00
Lucas Perais (lpe) 0f8c859d5f [FIX] component: error_handling when an error is rethrown 2021-12-20 16:55:50 +01:00
Géry Debongnie cca8438d38 [REF] components: remove .el 2021-12-20 14:43:21 +01:00
Géry Debongnie a727347d60 [REF] tests: improve test helpers
- remove snapshotApp
- remove addTemplates
- simplify helpers
- make sure snapshotted templates are snapshotted with the app config
2021-12-20 12:43:36 +01:00
Lucas Perais (lpe) f19de73b0f [FIX] fiber, lifecycle: trigger a render during the fiber.complete
Have a component which does a render in its onWillPatch, onPatched, onMounted hooks.

Before this commit, the result was incorrect: the second rendering was not taken into account.

After this commit, those renderings are correctly applied at the price of a delayed render when the fiber
is in a critical state.
2021-12-20 10:54:51 +01:00
Géry Debongnie a8ffcafd23 [FIX] blockdom: properly handle falsy attributes
This commit fixes some issues with falsy attributes not being properly
set/removed in various situations. Also, the behaviour was not
consistent between normal attribute (key/value) and generic attributes
(pair or object)
2021-12-16 15:07:27 +01:00
Géry Debongnie a2f01ef4ad [FIX] components: improve error handling
Owl provides a way to manage errors occuring in component lifecycle
methods. However, before this commit, these errors were not always
logged or visible, which is very annoying in the common developer
workflow (doing something, checking it works, seeing no error but a
broken interface).

In this commit, we make sure errors are logged/throws in all cases:

- if an error occurs in a mounting operation => the promise is rejected
(which will log the error)
- if an error occurs after the mounting operation and is not handled by
any error handlers => the error will be logged (with console.error).
Also, in that case, this commit adds a warning to explain that owl
destroys the root component, which will help developers understanding
what happened.
2021-12-16 13:29:31 +01:00
Géry Debongnie d7de25e867 [FIX] compiler: properly handle <t> tags in some cases
The problem was that the compiler is based on the assumption that the
multi block received by the parser only occurs in some cases
where the structure of the template require a multi block, and it does
not work when we have random multiblock elsewhere.

We could fix the issue by modifying the code generator code to support
these usecases, or by simply removing these cases in the parser. Since
this seems more efficient, this is the approach taken by this commit.

Note that it was a good opportunity to simplify the parser.
2021-12-15 15:32:56 +01:00
Géry Debongnie 424bc480e2 [DOC] update changelog 2021-12-14 16:03:10 +01:00
Géry Debongnie 421f400766 [DOC] reorganize doc, unskip test, fix some links 2021-12-14 09:45:25 +01:00
Géry Debongnie c37f3445f6 [FIX] blockdom: ignore attributes with undefined value 2021-12-13 15:07:03 +01:00
Géry Debongnie 4cde06d685 [IMP] component: add .bind suffix to props for easy binding 2021-12-13 12:43:23 +01:00
Lucas Perais (lpe) 7b95315b87 adapt to fix and to fixup 2021-12-13 12:39:43 +01:00
Géry Debongnie 09761ddd8e fixup, maybe 2021-12-13 12:39:43 +01:00
Lucas Perais (lpe) b3c6ec2b48 [FIX] component: render in delayed willUpdateProps
Have a child component on which a render is triggered.
This component delays its willUpdateProps and makes a rendering during the willUpdateProps

Before this commit, renderings of the child were inconsistent across
its parent's renderings.

After this commit, it works as expected.
2021-12-13 12:39:43 +01:00
Lucas Perais (lpe) b20462ada6 [FIX] app: factorize to allow smoother developments in projects
It should be usefull to allow developpers to implement mounting/unmounting
if they wish to.
2021-12-13 09:49:48 +01:00
Lucas Perais (lpe) 25000b36be [FIX] blockdom: do not propagate svg namespace to siblings 2021-12-03 14:01:44 +01:00
Lucas Perais (lpe) 144d4a253c [FIX] hooks: useSubEnv supports arbitrary descriptors in env
Before this commit, when defining a getter in the env passed to useSubEnv,
the value was read, losing the definition of the property.

After this commit, declaring a getter in the env works as expected:
the property stays a getter.
2021-12-03 08:06:29 +01:00
Lucas Perais (lpe) 9d48bda227 [FIX] app: support for arbitrary descriptors in env
Before this commit, when defining a getter in the env passed to the App,
the value was read, losing the definition of the property.

After this commit, declaring a getter in the env works as expected:
the property stays a getter.
2021-12-03 08:06:29 +01:00
Lucas Perais (lpe) c8db663869 [FIX] parser: correctly parse pre node within a div with new lines 2021-12-03 08:03:45 +01:00
Lucas Perais (lpe) 247200194f [FIX] component: error handling in class inheritance
Before this commit, class inheritance when using the onError hook was unclear nay wrong.

After this commit, error handlers are called from the bottom up  in the inheritance hierarchy.
If a handler doesn't rethrow the error, the handling stops there and no other handler is called.
If a handler does rethrow, the handlers declared in a parent class are executed.
2021-12-01 17:29:26 +01:00
Géry Debongnie 60b8817ac0 [REF] compiler: simplify all compiled templates 2021-12-01 17:25:31 +01:00
Géry Debongnie e008966291 [FIX] compiler: allow t-if with empty content 2021-12-01 13:22:32 +01:00
Géry Debongnie e2fedc6eff [REF] components: unskip concurrency test 2021-12-01 13:22:32 +01:00
Géry Debongnie b3fb9a35bf [FIX] move error handling out of fiber, fix complicated mounted issues 2021-12-01 12:07:26 +01:00
Géry Debongnie 240259568e [FIX] component: improve error handling
In the following situation: A parent of B, B parent of C, with an error
when C is mounted, caught by B and retriggering a rendering in B, then
the onMounted hook of A wasn't properly called. This commit fixes this
problem.
2021-12-01 12:07:26 +01:00
Géry Debongnie 18fb67eaad [FIX] blockdom: properly handle references
Before this commit, there were situations where the reference numbers
were not properly set, which caused the blocks generated to crash
because the algorithm could not get correct references.
2021-12-01 09:34:55 +01:00
Géry Debongnie e946967867 [IMP] component: render does not return a promise anymore 2021-11-30 08:03:17 +01:00
Géry Debongnie 777b2aae5e [IMP] add support for top level comments 2021-11-29 15:36:45 +01:00
Géry Debongnie 0831bb54e3 [REF] component: remove onDestroyed, implement onWillDestroy 2021-11-29 14:59:01 +01:00
Géry Debongnie 03f34a38dc [TESTS] test lifecycle in reactivity tests 2021-11-29 08:42:47 +01:00
Géry Debongnie dcefd26bee [REF] tests: improve useLogLifecycle and helpers 2021-11-29 08:42:47 +01:00
Samuel Degueldre 922dab8e8f [FIX] reactivity: only call clearReactivesForCallback once on unmount 2021-11-26 16:22:38 +01:00
Samuel Degueldre 99740f9993 [FIX] reactivity: fix memory leak 2021-11-26 15:55:02 +01:00
Géry Debongnie 0b79b1c8fd [FIX] compiler: readd template name in compiled code 2021-11-26 09:17:49 +01:00
Géry Debongnie 5b0dce94cf [REF] code_generator: move generating code to CodeTarget
Before this commit, we had two places with code that generate a function
code. Now, all this code is moved in a method 'generateCode' on
CodeTarget.
2021-11-26 09:17:49 +01:00
Géry Debongnie a1d435c5a5 [FIX] compiler: call dynamic templates with correct this 2021-11-25 17:29:30 +01:00
Géry Debongnie 13c3178760 [FIX] slots: properly bind this in t-on arrow functions 2021-11-25 14:45:30 +01:00
Géry Debongnie 2b565ce1b4 [REF] component: small cleanup
This commit makes it simpler to understand the way fibers are assigned
to nodes.
2021-11-25 11:34:23 +01:00
Géry Debongnie cfcf2c6714 [FIX] component: concurrency issue
When a parent and a child were rendered at the same time, it was
possible for the 2 renders to decrement the same fiber internal
counter, which meant that the render was stalled.
2021-11-25 11:12:31 +01:00
Samuel Degueldre 92174559a1 [FIX] slots: allow t-call and components in slot default content 2021-11-24 15:47:51 +01:00
Bruno Boi 840348892f [IMP] owl: upgrade rollup-plugin-typescript2 to version 0.31.1 2021-11-24 11:45:12 +01:00
Samuel Degueldre b988a68b6f [IMP] parser: normalize document before parsing 2021-11-24 11:19:45 +01:00
Samuel Degueldre 7869a1f2c6 [IMP] components: add test for template string in props 2021-11-24 10:09:52 +01:00
Samuel Degueldre 9e81d14d50 [IMP] parser: throw when using unsupported directive on component 2021-11-24 10:09:52 +01:00
Samuel Degueldre 0e4a55ba09 [FIX] components: allow prop names that are not valid bare property name 2021-11-24 10:09:52 +01:00
Mathieu Duckerts-Antoine 6e9b68dafa [FIX] props: prop names can contain - 2021-11-23 10:20:45 +01:00
Bruno Boi ba7c9063c0 Update CHANGELOG.md 2021-11-23 10:09:39 +01:00
Mathieu Duckerts-Antoine 3a361876ad [IMP] slots: via prop 'slots'
The slot inner working has been reworked. A prop "slots" is now passed
explicitely to the component. It looks like

{ slotName_1: slotInfo_1, ..., slotName_m: slotInfo_m }

with the objects slotInfo_i with mandatory keys "__render", "__ctx",
and optional key "__scope" and possibly others.

Here is how a slotInfo object can be created:
A slotInfo object is normally created by setting in a template something
like

<div>
    <t t-set-slot="foo" t-set-scope="scope" param_1="var" param_2="3">
        content
        <t t-esc="scope.bool"/>
        <t t-esc="scope.num"/>
    </t>
</div>

and it will be used somewhere like

<div>
    <t t-esc="props.slots.foo.param_1"/>
    <t t-slot="foo" bool="other_var" num="5">
</div>

In the above example, the function "__render" produces the block dom
element for the content of the t-set-slot.
The context "__ctx" will have a key "scope" with value { bool: ..., num: 5 }
and "__scope" will be set to "scope".
2021-11-22 16:36:43 +01:00
Samuel Degueldre 58b8572f0a [FIX] components: capture context in prop expressions 2021-11-22 13:18:17 +01:00
Samuel Degueldre c23637e6d8 [FIX] components: throw on duplicate t-key instead of hanging the app 2021-11-22 11:11:49 +01:00
Géry Debongnie 4a4b1fbba5 [IMP] app: add templates in app config
Also, improve the parsing code
2021-11-22 10:56:16 +01:00
Géry Debongnie 536d9e1762 [REM] tools: remove benchmarks/debug script
They are either no longer relevant, or less useful than some
alternatives (such as the js framework benchmark project)
2021-11-22 10:56:16 +01:00
Lucas Perais (lpe) 6ed68372a6 [FIX] package: bump owl version to 2.0.0-alpha1 2021-11-22 10:49:51 +01:00
Lucas Perais (lpe) efd934d2b1 [FIX] tools: adapt playground to owl 2 2021-11-19 16:11:28 +01:00
Lucas Perais (lpe) dabc24cee3 [FIX] index, reactivity: export reactive function in index 2021-11-19 16:11:28 +01:00
Samuel Degueldre 7f49796a07 [IMP] misc: update typescript to 4.5.2 2021-11-19 14:42:37 +01:00
Géry Debongnie 757dffefac [IMP] components: rename onRender->onWillRender, add onRendered 2021-11-19 13:26:44 +01:00
Samuel Degueldre bdfe058279 [IMP] reactivity: overhaul reactivity system
This commit makes the reactivity system more fine grained and makes it
more eager to stop observing keys or objects when they are modified,
this results in fewer "false positive" notifications.
2021-11-19 13:20:37 +01:00
Bruno Boi 345c44b952 fixup! [IMP] svg namespace support 2021-11-19 12:47:51 +01:00
Bruno Boi ea74739d46 [IMP] svg namespace support 2021-11-19 11:55:18 +01:00
Géry Debongnie 93b53d8017 [FIX] remove cyclic dependency, improve error typing (#982) 2021-11-18 10:44:49 +01:00
Lucas Perais (lpe) c2284bc6f5 [FIX] component, fiber: error_handling at the Fiber level
Before this commit, errors triggered at the level of the fiber (as opposed to at the level
of a component's rendering), were handled as the very top level of the rendering, that is,
in the scheduler.
This was wrong because components below in the rendering tree would not have a chance to handle their
children's or their own errors.

After this commit, error triggered in willPatch, onMounted and onPatched are correctly handled
at the closest component to where they were thrown.
2021-11-17 11:40:29 +01:00
Géry Debongnie 48650da62e [REM] remove some outdated tests 2021-11-17 09:21:55 +01:00
Géry Debongnie 2edd6cb8f3 [MOV] move memo and portal to root folder 2021-11-17 09:21:55 +01:00
Géry Debongnie 4b0a37c542 [REM] tests: remove async root tests 2021-11-17 09:21:55 +01:00
Lucas Perais (lpe) 50c0e30936 [FIX] component: error_handling on current component
Have a Child Compnent which has one component that succeeds and another
one that fails at its instanciation.
The Child component handles the Errors by rendering itself.

Before this commit, the error handling algorithm made impossible for the scheduler to finish.
This was because the current fiber was still counted as ongoing, when it was actually completed.

After this commit, this use case is handled correctly.
2021-11-16 17:05:25 +01:00
Géry Debongnie e97335d7ab [DOC] add a change log 2021-11-16 16:36:20 +01:00
Lucas Perais (lpe) 6ea40a66da [IMP] app, compiler: introduce t-out
t-out automatically escaped content when it is a string not marked
with the `markup` function

t-out renders the raw content if it is a Block, or if it has been marked
with the `markup` funtion.

t-esc has been kept since it is safe and is optimized to render text nodes.

all t-raw calls are in fact the same as t-out.
2021-11-16 14:37:53 +01:00
Lucas Perais (lpe) 2806cda420 [FIX][BREAKING] t-esc on component is not supported anymore 2021-11-16 14:37:53 +01:00
Bruno Boi cb0b525f32 [IMP] templates: can load multiple at once and also from XMLDocument 2021-11-16 13:10:09 +01:00
Mathieu Duckerts-Antoine 30d28670e8 [REF] reactivity: new API 2021-11-15 17:14:19 +01:00
Mathieu Duckerts-Antoine 7b761a2f8b [REF] tests: remove debugger 2021-11-15 17:14:19 +01:00
Mathieu Duckerts-Antoine d834bb2ac4 [FIX] reactivity: memory leak
The deletion of a key in an observed object did clear the observers of
that key but did not clear the atoms created (if any) when the key existed
(e.g. on the key value if it was trackable). This can lead to a growing
set of atoms that are useless but kept in memory if a lot of keys are
added/deleted. The same thing can happen if a key value is changed many
times and the values are trackable.

Here we fix the problem by
- keeping tracks of the objects that have been observed by an external call
  to the method "atom" (we call them seeds).
- remove all observer atoms that are not seeds for observers of at least
  one key deletion or key value change.

Note the fix is in some sense partial: a user could use the "atom" primitive
making the new system uncapable of avoiding a leak (see test "atom on an
object with a getter 3" where a getter is used in a weird way in an
observed object).
2021-11-15 17:14:19 +01:00
Bruno Boi 6ae92fa4b3 [IMP] hooks: reintroduce useExternalListener 2021-11-15 16:15:14 +01:00
Bruno Boi 3ceb118ace [IMP] hooks: introduce useEffect
Co-Authored-By: Samuel Degueldre <sad@odoo.com>
2021-11-15 16:07:25 +01:00
Bruno Boi fa426b7fc0 [IMP] owl: mount util now takes an AppConfig 2021-11-15 16:07:25 +01:00
Bruno Boi d7403871fc [IMP] env: env is now frozen, useSubEnv does not affect user env 2021-11-15 13:21:46 +01:00
Géry Debongnie 6aeff21d9a [FIX] tests: remove useless log 2021-11-15 09:35:13 +01:00
Géry Debongnie 429e145b68 [FIX] portal: unskip some tests 2021-11-15 09:35:13 +01:00
Géry Debongnie fb6aab8b70 [FIX] component: fix lifecycle order 2021-11-15 09:35:13 +01:00
Géry Debongnie 9e285e1e0a [FIX] unskip tests 2021-11-15 09:35:13 +01:00
Géry Debongnie 63bfdfb8db [REF] move event_bus into utils, readd 2 functions 2021-11-15 09:35:13 +01:00
Géry Debongnie d4c4fe853e [REM] tests: remove debug script tests 2021-11-15 09:35:13 +01:00
Géry Debongnie 9163bb1e08 [REF] app: move TemplateSet into its own file 2021-11-15 09:35:13 +01:00
Géry Debongnie a187a376a0 [REF] move app and compiler code around 2021-11-15 09:35:13 +01:00
Géry Debongnie 06ea6d2490 [REF] component: fix typescript error 2021-11-15 09:35:13 +01:00
Géry Debongnie 04334e23d7 [REF] compiler: rename qweb/ into compiler/ 2021-11-15 09:35:13 +01:00
Géry Debongnie 4efe1ae166 [IMP] reactivity: slightly improve code and typing 2021-11-15 09:35:13 +01:00
Lucas Perais (lpe) ad4673cb36 [IMP] component: re-introduce error handling in lifecycle 2021-11-12 16:54:40 +01:00
Samuel Degueldre e611c20ab4 [IMP] qweb: turn handlers into function expressions only
For the sake of consistency with vanilla JS, and to allow some things
that were previously not possible.
2021-11-12 13:17:43 +01:00
Mathieu Duckerts-Antoine d60a5a414e [IMP] reactivity: Context replacement
Aim to replace the abstraction "Context" from OWL 1 with the new primitives
"atom" and "useState":

- notification is done only after a batch of modifications.
- observers are notified at most once for a batch.
- an observer of type component is notified (and rerendered)
  only if it does not have an ancestor that has to be notified for the
  same batch of operations (anywhere in the web of references!).
- notification of components is done on all levels "simultaneously".

Co-authored-by: Aaron Bohy <aab@odoo.com>
Co-authored-by: Géry Debongnie <ged@odoo.com>
Co-authored-by: Mathieu Duckerts-Antoine <dam@odoo.com>
2021-11-10 15:08:08 +01:00
Géry Debongnie d47dcf6be2 [IMP] pin prettier version to ensure consistent results 2021-11-10 14:03:17 +01:00
Samuel Degueldre 3e70b17da1 [FIX] qweb: fix crash with ref an component in same slot 2021-11-10 13:36:13 +01:00
Samuel Degueldre c718d8e6c6 [FIX] qweb: fix crash when component only renders empty slot 2021-11-10 13:36:13 +01:00
Samuel Degueldre a122a94180 [IMP] qweb/components: remove t-ref on components
Refs to component expose a lot of implementation details that should be
private to parents. Parent to child communication should go through
props.
2021-11-10 13:36:13 +01:00
Bruno Boi ca139166ab [IMP] qweb: reintroduce t-tag directive
will not be compatible with t-model directive !
2021-11-09 16:36:03 +01:00
Lucas Perais (lpe) ae71db28e4 [IMP] qweb: compiler: support t-key on node and component without t-foreach 2021-11-09 14:53:19 +01:00
Bruno Boi 779fc6b02b fixup! [IMP] bdom: support multiple synthetic events on one node 2021-11-08 13:36:09 +01:00
Bruno Boi 84913593b2 [IMP] bdom: support multiple synthetic events on one node 2021-11-08 13:36:09 +01:00
Bruno Boi 8d4eb21536 [IMP] qweb/attributes: uncomment two tests
- textarea with t-att-value
- select with t-att-value
2021-11-08 11:25:14 +01:00
Bruno Boi 4bdfaf96f3 fixup! [IMP] qweb: introduce t-model directive 2021-11-03 15:25:21 +01:00
Bruno Boi 233c953243 [IMP] qweb: introduce t-model directive
supported modifiers: lazy, trim, number
2021-11-03 15:25:21 +01:00
Bruno Boi ee5ad354ff [IMP] tags: reintroduce inline css tag
The CSS tag is useful to define a css stylesheet in the javascript file:
```js
class MyComponent extends Component {
  static template = xml`
        <div class="my-component">some template</div>
    `;
  static style = css`
    .my-component {
      color: red;
    }
  `;
}
```

The `css` tag registers internally the css information. Then, whenever the first instance of the component is created, will add a <style> tag to the document <head>.

Original commit in Owl v1: 953778dc5
2021-11-03 10:07:14 +01:00
Mathieu Duckerts-Antoine 4e4b85e2a9 [IMP] reactivity: new primitives for reactivity
fine grained reactivity:

existing key in source changes --> only observer having read the key are notified

add/delete key in source changes --> all source observers are notified

Co-authored-by: Aaron Bohy <aab@odoo.com>
Co-authored-by: Géry Debongnie <ged@odoo.com>
Co-authored-by: Mathieu Duckerts-Antoine <dam@odoo.com>
2021-10-29 14:52:20 +02:00
Mathieu Duckerts-Antoine 6479631983 Code prettification 2021-10-29 14:52:20 +02:00
Lucas Perais (lpe) 2a0c410014 [IMP] qweb, blockdom, components: t-on with modifiers
supported modifiers: capture, prevent, stop, self.
2021-10-28 13:38:56 +02:00
Lucas Perais (lpe) bb4aecf638 [IMP] blockdom: t-on supports synthetic and native event handler
Synthetic handler is a sort of event delegation that allows placing
only one listener on the document to improve performance. It is an opt-in option.

Native listener places the listener on the node itself.
2021-10-28 13:38:56 +02:00
Lucas Perais (lpe) a1adfd5a1b [FIX] qweb, component: remove support for t-on on component node 2021-10-27 13:38:35 +02:00
Lucas Perais (lpe) dd4848f602 [FIX] qweb: t-key in t-foreach is mandatory, throws otherwise 2021-10-27 13:38:35 +02:00
Bruno Boi b25f476c22 [FIX] qweb: reintroduce test on t-debug 2021-10-26 09:33:56 +02:00
Mathieu Duckerts-Antoine 90cdb97b49 [IMP] tests: component mounting
We re-add some tests for component mounting.
2021-10-26 09:05:33 +02:00
Mathieu Duckerts-Antoine 7d7568d254 [IMP] app: mount app in "first-child" position
We reintroduce the possibility to mount the app in first position in
a target. The option "self" has been dropped since it is now possible
for a component to have several top level nodes.
2021-10-26 09:05:33 +02:00
Bruno Boi 06d207f60e [IMP] package.json: add watch arg to test:debug command
Before this commit
The command "npm run test:debug" runs the tests once.

After this commit
Jest runs in watch mode
2021-10-22 15:57:23 +02:00
Bruno Boi ff564421da [IMP] package.json: add remote test:debug command
Usage:
Open chrome://inspect then run in console:
> npm run test:debug ./path/to/your/testfile.ts
2021-10-22 14:51:23 +02:00
Mathieu Duckerts-Antoine c3695ec8db [IMP] component: defaultProps application
We re-add the application of defaultProps. Note that the application
is done twice in dev mode.
2021-10-22 12:46:57 +02:00
Mathieu Duckerts-Antoine 6e0834ce5e [IMP] component,qweb: props validation
We re-add the possibility to validate props when dev mode is active.
No change in the API right Now. The dev mode is activated via the
configure method of App class.
2021-10-22 12:46:57 +02:00
Samuel Degueldre d1425c7100 [FIX] component: correctly create a new node when previous is destroyed
Previously, when a component node had been created and destroyed, and
the corresponding component was then recreated, the destroyed node was
reused. This commit fixes that
2021-10-22 10:00:35 +02:00
Samuel Degueldre e01fe301c0 [IMP] *: re-add a bunch of tests 2021-10-21 10:34:54 +02:00
Mathieu Duckerts-Antoine ba2fe3ff55 [IMP] qweb: t-props directive
We reimplement the directive "t-props" and add some tests for it.
2021-10-20 15:52:43 +02:00
Géry Debongnie 700d574314 [MOV] move lifecycle_hooks into component/ 2021-10-20 15:16:28 +02:00
Mathieu Duckerts-Antoine ba32772a92 [FIX] qweb: re-add test on memory leak
Re-add test from 6e185f9.
2021-10-20 15:16:26 +02:00
Mathieu Duckerts-Antoine e4a0277f68 [FIX] qweb: t-set directive
This commit reintroduces some tests for the t-set directive and make
them pass. For that, it was necessary to adapt the qweb compiler in
order to get the following behaviors:

A t-set can affect parent contexts (up to the first parent tagged as
boundary) when the key changed is found in one of the parent contexts.
Some context are marked as boundaries in such a way that

   - rendering contexts (e.g. components) cannot be modified via a t-set.
   - a t-set in a t-call body or in a called template can never change a
     context above the t-call context.

Code prettification has been done.
Snapshots have been modified.
2021-10-20 15:16:25 +02:00
Lucas Perais (lpe) abe3748825 [FIX] re-introduce tests 2021-10-20 15:16:24 +02:00
Mathieu Duckerts-Antoine ee64de8b3f [IMP] qweb: throw error when t-component is not used with a 't' tag 2021-10-20 15:16:22 +02:00
Lucas Perais (lpe) ed831320db [FIX] re-introduce some missing tests
The point is to have visibility on the development of the owl2 features.
This commit reintroduces some tests keeping them skipped in order to fulfill that purpose.

There still are some missing tests though.
2021-10-20 15:16:21 +02:00
Mathieu Duckerts-Antoine 5f1e1e189f [IMP] qweb: re-add support of t-on directive
We add some test for the t-on directive.

For making them pass, it was necessary to change the code produced by
compileTForeach: the const declaration is not done by using generateId
and there was some conflict with the variable names produced in
captureExpression. Consequently, many snapshots had to be changed.

Code prettification has been done too.
2021-10-20 15:16:20 +02:00
Mathieu Duckerts-Antoine 55ef9ca116 [ADD] components: re-add a test for t-foreach directive 2021-10-20 15:16:18 +02:00
Samuel Degueldre 642ecf0ccd [IMP] components: re-add a bunch of components tests from owl 1
Some tests are skipped because they rely on not-yet-implemented
features.
2021-10-20 15:16:16 +02:00
Samuel Degueldre c9cc789f1d [IMP] components: add back style_class tests
The tests have been adapted to the new way of doing things, some tests
are skipped because they rely on features that are not implemented yet
2021-10-20 15:16:14 +02:00
Mathieu Duckerts-Antoine b8649f1add [ADD] Translation feature
This commit brings back the possibility to translate text nodes and
the attributes "label", "title", "placeholder", and "alt" in an app
configured with a suitable translation function.

It is also possible to deactivate translations under a node via
the directive t-translation="off".

For flexibility it is possible to define the list of translatable
attributes in the app.
2021-10-20 15:16:12 +02:00
Samuel Degueldre e2d98e4c26 [REF] doc: remove references to router and store
Owl 2 will not have a router or store implemented inside the library
2021-10-20 15:16:06 +02:00
Samuel Degueldre e6bb4ef286 [REF] qweb: use native Node and Element instead of custom Dom types 2021-10-20 15:11:32 +02:00
Géry Debongnie 7ac20f4fc2 [REF] initial prototype of owl 2 2021-10-20 15:11:25 +02:00
160 changed files with 5694 additions and 14149 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ name: Node.js CI
on:
pull_request:
branches: [ master ]
branches: [ master, owl-next ]
jobs:
build:
+66 -31
View File
@@ -31,7 +31,6 @@ All changes are documented here in no particular order.
- breaking: Support for inline css (`css` tag and static `style`) has been removed ([details](#37-support-for-inline-css-css-tag-and-static-style-has-been-removed))
- new: prop validation system can now describe that additional props are allowed (with `*`) ([doc](doc/reference/props.md#props-validation))
- breaking: prop validation system does not allow default prop on a mandatory (not optional) prop ([doc](doc/reference/props.md#props-validation))
- breaking: rendering a component does not necessarily render child components ([details](#40-rendering-a-component-does-not-necessarily-render-child-components))
@@ -44,6 +43,7 @@ All changes are documented here in no particular order.
- breaking: style/class on components are now regular props ([details](#10-styleclass-on-components-are-now-regular-props))
- new: components can use the `.bind` suffix to bind function props ([doc](doc/reference/props.md#binding-function-props))
- breaking: `t-on` does not accept expressions, only functions ([details](#30-t-on-does-not-accept-expressions-only-functions))
- breaking: `t-on` does not work on components any more ([details](#12-t-on-does-not-work-on-components-any-more))
- new: an error is thrown if an handler defined in a `t-on-` directive is not a function (failed silently previously in some cases)
- breaking: `t-component` no longer accepts strings ([details](#17-t-component-no-longer-accepts-strings))
- new: the `this` variable in template expressions is now bound to the component
@@ -80,6 +80,7 @@ All changes are documented here in no particular order.
- improved performance
- much simpler code
- new App class to encapsulate a root Owl component (with the config for that application) ([doc](doc/reference/app.md))
- new `Memo` component
- new `useEffect` hook ([doc](doc/reference/hooks.md#useeffect))
- breaking: `Context` is removed ([details](#15-context-is-removed))
- breaking: `env` is now totally empty ([details](#16-env-is-now-totally-empty))
@@ -247,12 +248,35 @@ Rationale: `shouldUpdate` is a dangerous method to use, that may cause a lot of
issues. Vue does not have such a mechanism (see https://github.com/vuejs/vue/issues/4255),
because the reactivity system in Vue is smart enough to only rerender the minimal
subset of components that is subscribed to a piece of state. Now, Owl 2 features
a much more powerful reactivity system, so the same rationale applies: in a way,
it's like each Owl 2 component has a `shouldUpdate` method that precisely tracks
every value used by the component.
a much more powerful reactivity system.
Migration code: remove the `shouldUpdate` methods, and it should work as well
as before.
Migration code: remove the `shouldUpdate` methods. Then, maybe the following
ideas may help:
- try to organize the state/architecture to minimize the number of state updates
- take advantage of the finer reactivity system. For example, if we have a list
of items, with a component for each item, we can write this:
```js
class Item extends Component {
setup() {
this.item = useState(this.props.item); // and only use this, not props.item
}
}
```
Doing so will make it that each `Item` component will register itself as an
observer of its own item, and will be the only component being rerendered when
its item object is updated.
- use the `Memo` component to wrap some piece of template. `Memo` memoize its
content, and only update itself if its props are different (shallow comparison):
```xml
<Memo a="state.a" b="state.b">
<t t-esc="state.a"/>
<t t-esc="state.b"/>
<t t-esc="state.c"/>
</Memo>
```
### 9. component.el is removed
@@ -312,6 +336,41 @@ Documentation:
- [Mounting a component](doc/reference/app.md#mount-helper)
### 12. `t-on` does not work on components any more
In owl 1, it was possible to bind an event listener on a component tag in a
template:
```xml
<SomeComponent t-on-some-event="doSomething"/>
```
This does not work any more.
Rationale: with the support of fragments, there is no longer a canonical html
element that we can refer. So, this makes it difficult to implement correctly
and efficiently. Also, we noticed in practice that the event system was an issue
in some cases, when components need to communicate before they are mounted. In
those cases, the better solution is to directly use a callback. Also, another
conceptual issue with this is that it kind of breaks the component encapsulation.
The child component kind of leak its own implementation to the outside world.
Migration: a quick fix that may work in some cases is to simply bind the event
handler on a parent htmlelement. A better way to do it, if possible, is to change
the component API to accept explicitely a callback as props.
```xml
<SomeComponent onSomeEvent="doSomething"/>
<!-- or alternatively: -->
<SomeComponent onSomeEvent.bind="doSomething"/>
```
Note that one of the example above uses the `.bind` suffix, to bind the function
prop to the component. Most of the time, binding the function is necessary, and
using the `.bind` suffix is very helpful in that case.
Documentation: [Binding function props](doc/reference/props.md#binding-function-props)
### 13. Portal does no longer transfer DOM events
In Owl 1, a Portal component would listen to events emitted on its portalled
@@ -739,28 +798,4 @@ Rationale: the `browser` object caused more trouble than it was worth. Also, it
seems like this should be done in user space, not at the framework level.
Migration: code should just be adapted to either use another browser object,
or to use native browser function (and then, just mock them directly).
## 40. Rendering a component does not necessarily render child components
Before, if one had the following component tree:
```mermaid
graph TD;
A-->B;
A-->C;
```
when `A` would render, it would also render `B` and `C`. Now, in Owl 2, it will
(shallow) compare the before and after props, and `B` or `C` will only be rerendered
if their props have changed.
Now, the question is what happens if the props have changed, but in a deeper way?
In that case, Owl will know, because each props are now reactive. So, if some
inner value read by `B` was changed, then only `B` will be updated.
Rationale: This was just not possible in Owl 1, but it now possible. This is
due to the rewriteof the underlying rendering engine and the reactivity
system. The goal is to have a big performance boost in large screen with many
components: now Owl only rerender what is strictly useful.
or to use native browser function (and then, just mock them directly).
+1 -4
View File
@@ -82,7 +82,6 @@ Are you new to Owl? This is the place to start!
- [Component](doc/reference/component.md)
- [Component Lifecycle](doc/reference/component.md#lifecycle)
- [Concurrency Model](doc/reference/concurrency_model.md)
- [Dev mode](doc/reference/app.md#dev-mode)
- [Dynamic sub components](doc/reference/component.md#dynamic-sub-components)
- [Environment](doc/reference/environment.md)
- [Error Handling](doc/reference/error_handling.md)
@@ -93,7 +92,6 @@ Are you new to Owl? This is the place to start!
- [Loading Templates](doc/reference/app.md#loading-templates)
- [Mounting a component](doc/reference/app.md#mount-helper)
- [Portal](doc/reference/portal.md)
- [Precompiling templates](doc/reference/precompiling_templates.md)
- [Props](doc/reference/props.md)
- [Props Validation](doc/reference/props.md#props-validation)
- [Reactivity](doc/reference/reactivity.md)
@@ -112,7 +110,6 @@ Are you new to Owl? This is the place to start!
- [Comparison with React/Vue](doc/miscellaneous/comparison.md)
- [Why did Odoo build Owl?](doc/miscellaneous/why_owl.md)
- [Changelog (from owl 1.x to 2.x)](CHANGELOG.md)
- [Notes on compiled templates](doc/miscellaneous/compiled_template.md)
## Installing Owl
@@ -124,5 +121,5 @@ npm install @odoo/owl
If you want to use a simple `<script>` tag, the last release can be downloaded here:
- [owl-1.4.10](https://github.com/odoo/owl/releases/tag/v1.4.10)
- [owl-1.4.7](https://github.com/odoo/owl/releases/tag/v1.4.7)
+2 -2
View File
@@ -240,7 +240,7 @@ class Task extends Component {
static template = xml /* xml */`
<div class="task" t-att-class="props.task.isCompleted ? 'done' : ''">
<input type="checkbox" t-att-checked="props.task.isCompleted"/>
<span><t t-esc="props.task.text"/></span>
<span><t t-esc="props.task.title"/></span>
</div>`;
static props = ["task"];
}
@@ -743,7 +743,7 @@ the user experience.
```xml
<input type="checkbox" t-att-checked="props.task.isCompleted"
t-att-id="props.task.id"
t-on-click="() => store.toggleTask(props.task)"/>
t-on-click="dispatch('toggleTask', props.task.id)"/>
<label t-att-for="props.task.id"><t t-esc="props.task.text"/></label>
```
-98
View File
@@ -1,98 +0,0 @@
# 🦉 Notes On Owl Compiled Templates 🦉
This page will explain what an Owl compiled template look like. This is a
technical document intended for developers interested in understanding how Owl
works internally.
Broadly speaking, Owl compiles templates into a javascript function (a closure)
that returns a function (the "render" function). The point of the closure is to
have a place to store all values specific to the template (in particular, "blocks").
Once a template is compiled, its closure function is called once to get the
render function, and from then on, only the render function is used.
The render function takes some context (and some additional information) and
return a virtual dom representation of the rendered template, as a block tree.
A block tree is a very light weight representation that only contains the dynamic
part of the template, and its structure. It is actually independant of the
static part of the templates (which are contained in the blocks captured by the
closure). This means that the work performed at render time is only to collect
dynamic data, and to describe the block structure of the result.
It looks like this, in pseudo code:
```js
function closure(bdom, helpers) {
// here is some place to put stuff specific to the template, such as
// blocks
...
return function render(context, node, key) {
// only build here all dynamic parts of the template
// build a block tree
return tree;
}
}
```
Now, let us see an example. Consider the following template:
```xml
<div class="some-class">
<div class="blabla">
<span><t t-esc="state.value"/></span>
</div>
<t t-if="state.info">
<p class="info" t-att-class="someAttribute">
<t t-esc="state.info"/>
</p>
</t>
<SomeComponent value="value"/>
</div>
```
If you look carefully, there are 5 dynamic things:
- a text value (the first `t-esc`),
- a sub block (the `t-if`),
- a dynamic attribute (the `t-att-class` attribute),
- another text value (the second `t-esc`),
- and finally, a sub component
Here is the compiled code for this template:
```js
function closure(bdom, helpers) {
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(
`<div class="some-class"><div class="blabla"><span><block-text-0/></span></div><block-child-0/><block-child-1/></div>`
);
let block2 = createBlock(`<p class="info" block-attribute-0="class"><block-text-1/></p>`);
return function render(ctx, node, key = "") {
let b2, b3;
let txt1 = ctx["state"].value;
if (ctx["state"].info) {
let attr1 = ctx["someAttribute"];
let txt2 = ctx["state"].info;
b2 = block2([attr1, txt2]);
}
b3 = component(`SomeComponent`, { value: ctx["value"] }, key + `__1`, node, ctx);
return block1([txt1], [b2, b3]);
};
}
```
The values captured in the closure capture the static part of the template: we
define here two blocks (which contains a template node, that can be deep cloned
whenever a block is mounted). Then the render function only describes the block
tree structure of the result, depending on the context. This means that we
minimize the amount of work done at render time.
Then, when we want to patch the dom, Owl will uses the `patch` function from
blockdom, which then will diff the block tree, and deep clone new blocks whenever
a new block is inserted, keep track of dynamic parts of each block, and update
them accordingly.
With this design, the cost of rendering a template is proportional to the number
of dynamic values, and not to the size of the template.
-1
View File
@@ -45,5 +45,4 @@ Utility/helpers:
- [`loadFile`](reference/utils.md#loadfile): an helper to load a file from the server
- [`markup`](reference/templates.md#outputting-data): utility function to define strings that represent html (should not be escaped)
- [`status`](reference/component.md#status-helper): utility function to get the status of a component (new, mounted or destroyed)
- [`validate`](reference/utils.md#validate): validates if an object satisfies a specified schema
- [`whenReady`](reference/utils.md#whenready): utility function to execute code when DOM is ready
+4 -15
View File
@@ -51,18 +51,17 @@ The `config` object is an object with some of the following keys:
- **`env (object)`**: if given, this will be the shared `env` given to each component
- **`props (object)`**: the props given to the root component
- **`dev (boolean, default=false)`**: if `true`, the application is rendered in
[`dev` mode](#dev-mode);
- **`dev (boolean, default=false)`**: if `true`, the application is rendered in `dev`
mode, which activates some additional checks (in particular, the props validation
code is only performed in dev mode)
- **`test (boolean, default=false)`**: `test` mode is the same as `dev` mode, except
that Owl will not log a message to warn that Owl is in `dev` mode.
that Owll will not log a message to warn that Owl is in `dev` mode.
- **`translatableAttributes (string[])`**: a list of additional attributes that should
be translated (see [translations](translations.md))
- **`translateFn (function)`**: a function that will be called by owl to translate
templates (see [translations](translations.md))
- **`templates (string | xml document)`**: all the templates that will be used by
the components created by the application.
- **`warnIfNoStaticProps (boolean, default=false)`**: if true, Owl will log a warning
whenever it encounters a component that does not provide a [static props description](props.md#props-validation).
## `mount` helper
@@ -111,13 +110,3 @@ const { loadFile, mount } = owl;
mount(Root, document.body, { env });
})();
```
## Dev mode
Dev mode activates some additional checks and developer amenities:
- [Props validation](./props.md#props-validation) is performed
- [t-foreach](./templates.md#loops) loops check for key unicity
- Lifecycle hooks are wrapped to report their errors in a more developer-friendly way
- onWillStart and onWillUpdateProps will emit a warning in the console when they
take longer than 3 seconds in an effort to ease debugging the presence of deadlocks
+2 -7
View File
@@ -68,17 +68,12 @@ The `Component` class has a very small API.
component will go through the following lifecycle methods: `willUpdateProps`,
`willPatch` and `patched`.
* **`render(deep[=false])`**: calling this method directly will cause a rerender. Note
* **`render()`**: calling this method directly will cause a rerender. Note
that with the reactivity system, this should be rare to have to do it manually.
Also, the rendering operation is asynchronous, so the DOM will only be updated
slightly later (at the next animation frame, if no component delays the
rendering)
By default, the render initiated by this method will stop at each child
component if their props are (shallow) equal. To force a render to update
all child components, one can use the optional `deep` argument. Note that the
value of the `deep` argument needs to be a boolean, not a truthy value.
## Static Properties
- **`template (string)`**: this is the name of the template that
@@ -357,7 +352,7 @@ cleaning operation, since the component may be destroyed before it has even been
mounted. The `willDestroy` hook is useful in that situation, since it is always
called.
The `onWillUnmount` hook is used to register a function that will be executed at
The `onWillDestroy` hook is used to register a function that will be executed at
this moment:
```javascript
+105
View File
@@ -0,0 +1,105 @@
# 🦉 Context 🦉
## Content
- [Overview](#overview)
- [Example](#example)
- [Reference](#reference)
- [`Context`](#context)
- [`useContext`](#usecontext)
## Overview
The `Context` object provides a way to share data between an arbitrary number
of components. Usually, data is passed from a parent to its children component,
but when we have to deal with some mostly global information, this can be
annoying, since each component will need to pass the information to each children,
even though some or most of them will not use the information.
With a `Context` object, each component can subscribe (with the `useContext` hook)
to its state, and will be updated whenever the context state is updated.
## Example
Assume that we have an application with various components which needs to render
differently depending on the size of the device. Here is how we could proceed
to make sure that the information is properly shared. First, let us create a
context, and add it to the environment:
```js
const deviceContext = new Context({ isMobile: true });
App.env.deviceContext = deviceContext;
```
If we want to make it completely responsive, we need to update its value whenever
the size of the screen is updated:
```js
const isMobile = () => window.innerWidth <= 768;
window.addEventListener(
"resize",
owl.utils.debounce(() => {
const state = deviceContext.state;
if (state.isMobile !== isMobile()) {
state.isMobile = !state.isMobile;
}
}, 15)
);
```
Then, each component that want can subscribe and render differently depending on the
fact that we are in a mobile or desktop mode.
```js
class SomeComponent extends Component {
static template = xml`
<div>
<t t-if=device.isMobile>
some simplified user interface
</t>
<t t-else="">
a more advanced user interface
</t>
</div>`;
device = useContext(this.env.deviceContext);
}
```
## Reference
### `Context`
A `Context` object should be created with a state object:
```js
const someContext = new Context({ some: "key" });
```
Its state is now available in the `state` key:
```js
someContext.state.some = "other key";
```
This is the way some global code (such as the responsive code above) should
read and update the context state. However, components should not ever read the
context state directly from the context, they should instead use the `useContext`
hook to properly register themselves to state changes.
Note that the `Context` hook is different from the React version. For example,
there is no concept of provider/consumer. So, the `Context` feature does not
by itself allow the use of a different context state depending on the component
place in the component tree. However, this functionality can be obtained, if
necessary, with the use of sub environment.
### `useContext`
The `useContext` hook is the normal way for a component to register themselve
to context state changes. The `useContext` method returns the context state:
```js
device = useContext(this.env.deviceContext);
```
It is a simple observed state (with an owl `Observer`), which contains the shared
information.
+25
View File
@@ -0,0 +1,25 @@
# 🦉 Event Bus 🦉
It is sometimes useful to use a `Bus` to communicate informations between various
parts of the code. Owl has a very simple bus class, which manages subscriptions,
triggering events, and callbacks.
```js
const bus = new owl.core.EventBus();
bus.on("some-event", null, function (...args) {
console.log(...args);
});
bus.trigger("some-event", 1, 2, 3);
// [1,2,3] will be logged to the console
```
Its API is:
| Method | Description |
| -------------------------------- | --------------------------------- |
| `on(eventType, owner, callback)` | add a listener |
| `off(eventType, owner)` | remove all listeners for an owner |
| `trigger(eventType, ...args)` | trigger an event |
| `clear` | remove all subscriptions |
-17
View File
@@ -5,7 +5,6 @@
- [Event Handling](#event-handling)
- [Modifiers](#modifiers)
- [Synthetic Events](#synthetic-events)
- [On Components](#on-components)
## Event Handling
@@ -101,19 +100,3 @@ To enable it, one can just use the `.synthetic` suffix:
</t>
</div>
```
## On Components
The `t-on` directive also works on a child component:
```xml
<div>
in some template
<Child t-on-click="dosomething"/>
</div>
```
This will catch all click events on any html element contained in the `Child`
sub component. Note that if the child component is reduced to one (or more) text
nodes, then clicking on it will not call the handler, since the event will be
dispatched by the browser on the parent element (a `div` in this case).
-30
View File
@@ -1,30 +0,0 @@
# 🦉 Precompiling templates 🦉
Owl is designed to be used by the Odoo javascript framework. Since Odoo handles
its assets in its own non standard way, it was decided/assumed that Owl would
compile templates at runtime.
However, in some cases, it is not optimal, or even worse, not possible to do that.
For example, browser extensions do not allow javascript code to create a new
function (using the `new Function(...)` syntax).
Therefore, in these cases, it is required to compile templates ahead of time. It
is possible to do that in Owl, but the tooling is still rough. For now, the
process is the following:
1. write your templates in xml files (with a `t-name` directive to declare the name
of the template)
2. Compile them in a `templates.js` file
3. get the `owl.iife.runtime.js` file (which is a owl build without the compiler)
4. bundle `owl.iife.runtime.js` and `template.js` with your assets (owl needs to
be positioned before the templates)
Here is a more detailed explanation on how to compile xml files into a js file:
1. clone the owl repository locally
2. `npm install` to install all the required tooling
3. `npm run build:runtime` to build the `owl.iife.runtime.js` file
4. `npm run build:compiler` to build the template compiler
5. `npm run compile_templates -- path/to/your/templates` will scan your target
folder, find all xml files, get all templates, compile them, and generate a
`templates.js` file.
-4
View File
@@ -159,7 +159,6 @@ For each key, a `prop` definition is either a boolean, a constructor, a list of
- a boolean: indicate that the props exists, and is mandatory.
- a constructor: this should describe the type, for example: `id: Number` describe
the props `id` as a number
- an object describing a value as type. This is done by using the `value` key. For example, `{value: false}` specifies that the corresponding value should be equal to false.
- a list of constructors. In that case, this means that we allow more than one
type. For example, `id: [Number, String]` means that `id` can be either a string
or a number.
@@ -241,12 +240,9 @@ class ComponentB extends owl.Component {
size: {
validate: e => ["small", "medium", "large"].includes(e)
},
someId: [Number, {value: false}], // either a number or false
};
```
Note: the props validation code is done by using the [validate utility function](utils.md#validate).
## Good Practices
A `props` object is a collection of values that come from the parent. As such,
-12
View File
@@ -25,11 +25,6 @@ To solve this issue, Owl provides two reactivity primitives:
Most of the time, the `useState` hook is the best solution.
Since version 2.0, Owl applies the fine grained reactivity at the component
level: reactive objects received as props are automatically subscribed to by the
component, so Owl can track which part of these props are consumed by each
component, and is therefore able to only rerender the impacted components.
## `useState`
Let us start by an example of how `useState` could be used:
@@ -122,10 +117,3 @@ rawState.value = 3; // will NOT be picked up by the reactivity system!!!
Here again, this is useful in some situations where we want to explicitely bypass
Owl, but using this function means that the responsability of coordinating
state update is given to the user code, instead of Owl. Subtle bugs may arise!
Also, normal (non-reactive objects) will be directly returned by `toRaw`:
```js
const obj = { a: 1 };
console.log(toRaw(obj) === obj); // true
```
-6
View File
@@ -239,12 +239,6 @@ And the child component that includes the slot can provide values like this:
<t t-slot="foo" bool="other_var" num="5">
```
or this:
```xml
<t t-slot="foo" t-props="someObject">
```
In the case of the default slot, you may declare the slot scope directly on the
component itself:
+2 -23
View File
@@ -21,7 +21,6 @@
- [Fragments](#fragments)
- [Inline templates](#inline-templates)
- [Rendering svg](#rendering-svg)
- [Restrictions](#restrictions)
## Overview
@@ -290,11 +289,10 @@ If an expression evaluates to a falsy value, it will not be set at all:
It is sometimes convenient to format an attribute with string interpolation. In
that case, the `t-attf-` directive can be used. It is useful when we need to mix
literal and dynamic elements, such as css classes. The dynamic elements can be
specified with either `{{...}}` or `#{...}`:
literal and dynamic elements, such as css classes.
```xml
<div t-attf-foo="a {{value1}} is #{value2} of {{value3}} ]"/>
<div t-attf-foo="a {{value1}} is {{value2}} of {{value3}} ]"/>
<!-- result if values are set to 1,2 and 3: <div foo="a 0 is 1 of 2 ]"></div> -->
```
@@ -540,15 +538,6 @@ This can be used to define variables scoped to a sub template:
<!-- "var" does not exist here -->
```
Note: by default, the rendering context for a sub template is simply the current
rendering context (so, the current component). However, it may be useful to be
able to specify a specific object as context. This can be done by using the
`t-call-context` directive:
```xml
<t t-call="other-template" t-call-context="obj"/>
```
### Dynamic sub templates
The `t-call` directive can also be used to dynamically call a sub template,
@@ -691,13 +680,3 @@ if a template is supposed to be included in a svg namespace or not. Therefore,
Owl depends on a heuristic: if a tag is either `svg`, `g` or `path`, then it will
be considered as svg. In practice, this means that each component or each sub
templates (included with `t-call`) should have one of these tag as root tag.
## Restrictions
Note that Owl templates forbid the use of tag and or attributes starting with
the `block-` string. This restriction prevents name collision with the internal
code of Owl.
```xml
<div><block-1>this will not be accepted by Owl</block-1></div>
```
-22
View File
@@ -8,7 +8,6 @@ functions are all available in the `owl.utils` namespace.
- [`whenReady`](#whenready): executing code when DOM is ready
- [`loadFile`](#loadfile): loading a file (useful for templates)
- [`EventBus`](#eventbus): a simple EventBus
- [`validate`](#validate): a validation function
## `whenReady`
@@ -57,24 +56,3 @@ bus.addEventListener("event", () => console.log("something happened"));
bus.trigger("event"); // 'something happened' is logged
```
## `validate`
The `validate` function is a function that validates if a given object satisfies a
specified schema. It is actually used by Owl itself to perform
[props validation](props.md#props-validation). For example:
```js
validate(
{ a: "hey" },
{
id: Number,
url: [Boolean, { type: Array, element: Number }],
}
);
// throws an error with the following information:
// - unknown key 'a',
// - 'id' is missing (should be a number),
// - 'url' is missing (should be a boolean or list of numbers),
```
+3 -7
View File
@@ -1,11 +1,11 @@
{
"name": "@odoo/owl",
"version": "2.0.0-beta-10",
"version": "2.0.0-alpha.1",
"description": "Odoo Web Library (OWL)",
"main": "dist/owl.cjs.js",
"browser": "dist/owl.iife.js",
"module": "dist/owl.es.js",
"types": "dist/types/owl.d.ts",
"types": "dist/types/index.d.ts",
"files": [
"dist"
],
@@ -14,8 +14,6 @@
},
"scripts": {
"build:bundle": "rollup -c --failAfterWarnings",
"build:runtime": "rollup -c --failAfterWarnings runtime",
"build:compiler": "rollup -c --failAfterWarnings compiler",
"build": "npm run build:bundle",
"test": "jest",
"test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand --watch --testTimeout=5000000",
@@ -27,8 +25,7 @@
"prettier": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md} --write",
"check-formatting": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md} --check",
"publish": "npm run build && npm publish",
"release": "node tools/release.js",
"compile_templates": "node tools/compile_xml.js"
"release": "node tools/release.js"
},
"repository": {
"type": "git",
@@ -55,7 +52,6 @@
"npm-run-all": "^4.1.5",
"prettier": "2.4.1",
"rollup": "^2.56.3",
"rollup-plugin-dts": "^4.2.2",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.31.1",
"sass": "^1.16.1",
+1 -1
View File
@@ -1,6 +1,6 @@
# 🦉 OWL Roadmap 🦉
- Current version: 1.4.10
- Current version: 1.4.7
- Status: stable
This roadmap is only an attempt at predicting Owl's future. Everything may
+35 -50
View File
@@ -2,10 +2,14 @@ import pkg from "./package.json";
import git from "git-rev-sync";
import typescript from 'rollup-plugin-typescript2';
import { terser } from "rollup-plugin-terser";
import dts from "rollup-plugin-dts";
let input, output;
const name = "owl";
const extend = true;
/**
* Meta data to be added on the __info__ object.
* Used to let external tools know the current owl version.
*/
const outro = `
__info__.version = '${pkg.version}';
__info__.date = '${new Date().toISOString()}';
@@ -13,39 +17,13 @@ __info__.hash = '${git.short()}';
__info__.url = 'https://github.com/odoo/owl';
`;
switch (process.argv[4]) {
case "compiler":
input = "src/compiler/index.ts",
output = [
getConfigForFormat('cjs', 'dist/compiler.js', ''),
]
break;
case "runtime":
input = "src/runtime/index.ts";
output = [
getConfigForFormat('esm', addSuffix(pkg.module, 'runtime'), outro),
getConfigForFormat('cjs', addSuffix(pkg.main, 'runtime'), outro),
getConfigForFormat('iife', addSuffix(pkg.browser, 'runtime'), outro),
getConfigForFormat('iife', addSuffix(pkg.browser, 'runtime'), outro, true),
]
break;
default:
input = "src/index.ts",
output = [
getConfigForFormat('esm', pkg.module, outro),
getConfigForFormat('cjs', pkg.main, outro),
getConfigForFormat('iife', pkg.browser, outro),
getConfigForFormat('iife', pkg.browser, outro, true),
]
}
/**
* Generate from a string depicting a path a new path for the minified version.
* @param {string} pkgFileName file name
*/
function addSuffix(pkgFileName, suffix) {
function generateMinifiedNameFromPkgName(pkgFileName) {
const parts = pkgFileName.split('.');
parts.splice(parts.length - 1, 0, suffix);
parts.splice(parts.length - 1, 0, "min");
return parts.join('.');
}
@@ -55,12 +33,12 @@ function addSuffix(pkgFileName, suffix) {
* @param {string} generatedFileName generated file name
* @param {boolean} minified should it be minified
*/
function getConfigForFormat(format, generatedFileName, outro, minified = false) {
function getConfigForFormat(format, generatedFileName, minified = false) {
return {
file: minified ? addSuffix(generatedFileName, "min") : generatedFileName,
file: minified ? generateMinifiedNameFromPkgName(generatedFileName) : generatedFileName,
format: format,
name: "owl",
extend: true,
name: name,
extend: extend,
outro: outro,
freeze: false,
plugins: minified ? [terser()] : [],
@@ -68,19 +46,26 @@ function getConfigForFormat(format, generatedFileName, outro, minified = false)
};
}
export default [
{
input,
output,
plugins: [
typescript({
useTsconfigDeclarationDir: true
}),
]
},
{
input: "dist/types/index.d.ts",
output: [{ file: "dist/types/owl.d.ts", format: "es" }],
plugins: [dts()],
},
];
export default {
input: "src/index.ts",
output: [
/**
* Read about module formats:
* https://auth0.com/blog/javascript-module-systems-showdown/
* https://medium.com/@kelin2025/so-you-wanna-use-es6-modules-714f48b3a953
*/
getConfigForFormat('esm', pkg.module),
getConfigForFormat('esm', pkg.module, true),
getConfigForFormat('cjs', pkg.main),
getConfigForFormat('cjs', pkg.main, true),
getConfigForFormat('iife', pkg.browser),
getConfigForFormat('iife', pkg.browser, true),
],
plugins: [
typescript({
useTsconfigDeclarationDir: true
}),
]
};
+107
View File
@@ -0,0 +1,107 @@
import { Component, ComponentConstructor } from "../component/component";
import { ComponentNode } from "../component/component_node";
import { MountOptions } from "../component/fibers";
import { Scheduler } from "../component/scheduler";
import { TemplateSet, TemplateSetConfig } from "./template_set";
import { nodeErrorHandlers } from "../component/error_handling";
import { validateTarget } from "../utils";
// reimplement dev mode stuff see last change in 0f7a8289a6fb8387c3c1af41c6664b2a8448758f
export interface Env {
[key: string]: any;
}
export interface AppConfig<P, E> extends TemplateSetConfig {
props?: P;
env?: E;
test?: boolean;
}
export const DEV_MSG = `Owl is running in 'dev' mode.
This is not suitable for production use.
See https://github.com/odoo/owl/blob/master/doc/reference/config.md#mode for more information.`;
export class App<
T extends abstract new (...args: any) => any = any,
P = any,
E = any
> extends TemplateSet {
static validateTarget = validateTarget;
Root: ComponentConstructor<P, E>;
props: P;
env: E;
scheduler = new Scheduler();
root: ComponentNode<P, E> | null = null;
constructor(Root: ComponentConstructor<P, E>, config: AppConfig<P, E> = {}) {
super(config);
this.Root = Root;
if (config.test) {
this.dev = true;
}
if (this.dev && !config.test) {
console.info(DEV_MSG);
}
const descrs = Object.getOwnPropertyDescriptors(config.env || {});
this.env = Object.freeze(Object.defineProperties({}, descrs)) as E;
this.props = config.props || ({} as P);
}
mount(target: HTMLElement, options?: MountOptions): Promise<Component<P, E> & InstanceType<T>> {
App.validateTarget(target);
const node = this.makeNode(this.Root, this.props);
const prom = this.mountNode(node, target, options);
this.root = node;
return prom;
}
makeNode(Component: ComponentConstructor, props: any): ComponentNode {
return new ComponentNode(Component, props, this);
}
mountNode(node: ComponentNode, target: HTMLElement, options?: MountOptions) {
const promise: any = new Promise((resolve, reject) => {
let isResolved = false;
// manually set a onMounted callback.
// that way, we are independant from the current node.
node.mounted.push(() => {
resolve(node.component);
isResolved = true;
});
// Manually add the last resort error handler on the node
let handlers = nodeErrorHandlers.get(node);
if (!handlers) {
handlers = [];
nodeErrorHandlers.set(node, handlers);
}
handlers.unshift((e) => {
if (isResolved) {
console.error(e);
} else {
reject(e);
}
throw e;
});
});
node.mountComponent(target, options);
return promise;
}
destroy() {
if (this.root) {
this.root.destroy();
}
}
}
export async function mount<T extends abstract new (...args: any) => any = any, P = any, E = any>(
C: T & ComponentConstructor<P, E>,
target: HTMLElement,
config: AppConfig<P, E> & MountOptions = {}
): Promise<Component<P, E> & InstanceType<T>> {
return new App(C, config).mount(target, config);
}
@@ -1,11 +1,7 @@
import { BDom, multi, text, toggler, createCatcher } from "./blockdom";
import { Markup } from "./utils";
import { html } from "./blockdom/index";
import { isOptional, validateSchema } from "./validation";
import type { ComponentConstructor } from "./component";
import { markRaw } from "./reactivity";
const ObjectCreate = Object.create;
import { BDom, multi, text, toggler } from "../blockdom";
import { validateProps } from "../component/props_validation";
import { Markup } from "../utils";
import { html } from "../blockdom/index";
/**
* This file contains utility functions that will be injected in each template,
* to perform various useful tasks in the compiled code.
@@ -25,11 +21,11 @@ function callSlot(
defaultContent?: (ctx: any, node: any, key: string) => BDom
): BDom {
key = key + "__slot_" + name;
const slots = ctx.props.slots || {};
const slots = (ctx.props && ctx.props.slots) || {};
const { __render, __ctx, __scope } = slots[name] || {};
const slotScope = ObjectCreate(__ctx || {});
const slotScope = Object.create(__ctx || {});
if (__scope) {
slotScope[__scope] = extra;
slotScope[__scope] = extra || {};
}
const slotBDom = __render ? __render.call(__ctx.__owl__.component, slotScope, parent, key) : null;
if (defaultContent) {
@@ -47,7 +43,7 @@ function callSlot(
function capture(ctx: any): any {
const component = ctx.__owl__.component;
const result = ObjectCreate(component);
const result = Object.create(component);
for (let k in ctx) {
result[k] = ctx[k];
}
@@ -108,17 +104,15 @@ function shallowEqual(l1: any[], l2: any[]): boolean {
class LazyValue {
fn: any;
ctx: any;
component: any;
node: any;
constructor(fn: any, ctx: any, component: any, node: any) {
constructor(fn: any, ctx: any, node: any) {
this.fn = fn;
this.ctx = capture(ctx);
this.component = component;
this.node = node;
}
evaluate(): any {
return this.fn.call(this.component, this.ctx, this.node);
return this.fn(this.ctx, this.node);
}
toString() {
@@ -135,49 +129,36 @@ export function safeOutput(value: any): ReturnType<typeof toggler> {
}
let safeKey;
let block;
switch (typeof value) {
case "object":
if (value instanceof Markup) {
safeKey = `string_safe`;
block = html(value as string);
} else if (value instanceof LazyValue) {
safeKey = `lazy_value`;
block = value.evaluate();
} else if (value instanceof String) {
safeKey = "string_unsafe";
block = text(value);
} else {
// Assuming it is a block
safeKey = "block_safe";
block = value;
}
break;
case "string":
safeKey = "string_unsafe";
block = text(value);
break;
default:
safeKey = "string_unsafe";
block = text(String(value));
if (value instanceof Markup) {
safeKey = `string_safe`;
block = html(value as string);
} else if (value instanceof LazyValue) {
safeKey = `lazy_value`;
block = value.evaluate();
} else if (value instanceof String || typeof value === "string") {
safeKey = "string_unsafe";
block = text(value);
} else {
// Assuming it is a block
safeKey = "block_safe";
block = value;
}
return toggler(safeKey, block);
}
let boundFunctions = new WeakMap();
const WeakMapGet = WeakMap.prototype.get;
const WeakMapSet = WeakMap.prototype.set;
function bind(ctx: any, fn: Function): Function {
let component = ctx.__owl__.component;
let boundFnMap = WeakMapGet.call(boundFunctions, component);
let boundFnMap = boundFunctions.get(component);
if (!boundFnMap) {
boundFnMap = new WeakMap();
WeakMapSet.call(boundFunctions, component, boundFnMap);
boundFunctions.set(component, boundFnMap);
}
let boundFn = WeakMapGet.call(boundFnMap, fn);
let boundFn = boundFnMap.get(fn);
if (!boundFn) {
boundFn = fn.bind(component);
WeakMapSet.call(boundFnMap, fn, boundFn);
boundFnMap.set(fn, boundFn);
}
return boundFn;
}
@@ -200,53 +181,7 @@ function multiRefSetter(refs: RefMap, name: string): RefSetter {
};
}
/**
* Validate the component props (or next props) against the (static) props
* description. This is potentially an expensive operation: it may needs to
* visit recursively the props and all the children to check if they are valid.
* This is why it is only done in 'dev' mode.
*/
export function validateProps<P>(name: string | ComponentConstructor<P>, props: P, parent?: any) {
const ComponentClass =
typeof name !== "string"
? name
: (parent.constructor.components[name] as ComponentConstructor<P> | undefined);
if (!ComponentClass) {
// this is an error, wrong component. We silently return here instead so the
// error is triggered by the usual path ('component' function)
return;
}
const schema = ComponentClass.props;
if (!schema) {
if (parent.__owl__.app.warnIfNoStaticProps) {
console.warn(`Component '${ComponentClass.name}' does not have a static props description`);
}
return;
}
const defaultProps = ComponentClass.defaultProps;
if (defaultProps) {
let isMandatory = (name: string) =>
Array.isArray(schema)
? schema.includes(name)
: name in schema && !("*" in schema) && !isOptional(schema[name]);
for (let p in defaultProps) {
if (isMandatory(p)) {
throw new Error(
`A default value cannot be defined for a mandatory prop (name: '${p}', component: ${ComponentClass.name})`
);
}
}
}
const errors = validateSchema(props, schema);
if (errors.length) {
throw new Error(`Invalid props for component '${ComponentClass.name}': ` + errors.join(", "));
}
}
export const helpers = {
export const UTILS = {
withDefault,
zero: Symbol("zero"),
isBoundary,
@@ -262,6 +197,4 @@ export const helpers = {
LazyValue,
safeOutput,
bind,
createCatcher,
markRaw,
};
@@ -1,10 +1,11 @@
import { compile, Template, TemplateFunction } from "../compiler";
import { comment, createBlock, html, list, multi, text, toggler } from "./blockdom";
import { getCurrent } from "./component_node";
import { Portal, portalTemplate } from "./portal";
import { helpers } from "./template_helpers";
import { createBlock, html, list, multi, text, toggler, comment } from "../blockdom";
import { compile, Template } from "../compiler";
import { component } from "../component/component_node";
import { UTILS } from "./template_helpers";
const bdom = { text, createBlock, list, multi, html, toggler, comment };
const bdom = { text, createBlock, list, multi, html, toggler, component, comment };
export const globalTemplates: { [key: string]: string | Element } = {};
function parseXML(xml: string): Document {
const parser = new DOMParser();
@@ -44,15 +45,18 @@ export interface TemplateSetConfig {
}
export class TemplateSet {
static registerTemplate(name: string, fn: TemplateFunction) {
globalTemplates[name] = fn;
}
dev: boolean;
rawTemplates: typeof globalTemplates = Object.create(globalTemplates);
templates: { [name: string]: Template } = {};
translateFn?: (s: string) => string;
translatableAttributes?: string[];
Portal = Portal;
utils: typeof UTILS = Object.assign({}, UTILS, {
call: (owner: any, subTemplate: string, ctx: any, parent: any, key: any) => {
const template = this.getTemplate(subTemplate);
return toggler(subTemplate, template.call(owner, ctx, parent, key));
},
getTemplate: (name: string) => this.getTemplate(name),
});
constructor(config: TemplateSetConfig = {}) {
this.dev = config.dev || false;
@@ -63,25 +67,18 @@ export class TemplateSet {
}
}
addTemplate(name: string, template: string | Element) {
if (name in this.rawTemplates) {
const rawTemplate = this.rawTemplates[name];
const currentAsString =
typeof rawTemplate === "string"
? rawTemplate
: rawTemplate instanceof Element
? rawTemplate.outerHTML
: rawTemplate.toString();
const newAsString = typeof template === "string" ? template : template.outerHTML;
if (currentAsString === newAsString) {
return;
}
throw new Error(`Template ${name} already defined with different content`);
addTemplate(
name: string,
template: string | Element,
options: { allowDuplicate?: boolean } = {}
) {
if (name in this.rawTemplates && !options.allowDuplicate) {
throw new Error(`Template ${name} already defined`);
}
this.rawTemplates[name] = template;
}
addTemplates(xml: string | Document) {
addTemplates(xml: string | Document, options: { allowDuplicate?: boolean } = {}) {
if (!xml) {
// empty string
return;
@@ -89,7 +86,7 @@ export class TemplateSet {
xml = xml instanceof Document ? xml : parseXML(xml);
for (const template of xml.querySelectorAll("[t-name]")) {
const name = template.getAttribute("t-name")!;
this.addTemplate(name, template);
this.addTemplate(name, template, options);
}
}
@@ -97,42 +94,34 @@ export class TemplateSet {
if (!(name in this.templates)) {
const rawTemplate = this.rawTemplates[name];
if (rawTemplate === undefined) {
let extraInfo = "";
try {
const componentName = getCurrent().component.constructor.name;
extraInfo = ` (for component "${componentName}")`;
} catch {}
throw new Error(`Missing template: "${name}"${extraInfo}`);
throw new Error(`Missing template: "${name}"`);
}
const isFn = typeof rawTemplate === "function" && !(rawTemplate instanceof Element);
const templateFn = isFn ? rawTemplate : this._compileTemplate(name, rawTemplate);
const templateFn = this._compileTemplate(name, rawTemplate);
// first add a function to lazily get the template, in case there is a
// recursive call to the template name
const templates = this.templates;
this.templates[name] = function (context, parent) {
return templates[name].call(this, context, parent);
};
const template = templateFn(this, bdom, helpers);
const template = templateFn(bdom, this.utils);
this.templates[name] = template;
}
return this.templates[name];
}
_compileTemplate(name: string, template: string | Element): ReturnType<typeof compile> {
throw new Error(`Unable to compile a template. Please use owl full build instead`);
}
callTemplate(owner: any, subTemplate: string, ctx: any, parent: any, key: any): any {
const template = this.getTemplate(subTemplate);
return toggler(subTemplate, template.call(owner, ctx, parent, key));
_compileTemplate(name: string, template: string | Element) {
return compile(template, {
name,
dev: this.dev,
translateFn: this.translateFn,
translatableAttributes: this.translatableAttributes,
});
}
}
// -----------------------------------------------------------------------------
// xml tag helper
// -----------------------------------------------------------------------------
export const globalTemplates: { [key: string]: string | Element | TemplateFunction } = {};
export function xml(...args: Parameters<typeof String.raw>) {
const name = `__template__${xml.nextId++}`;
const value = String.raw(...args);
@@ -141,5 +130,3 @@ export function xml(...args: Parameters<typeof String.raw>) {
}
xml.nextId = 1;
TemplateSet.registerTemplate("__portal__", portalTemplate);
@@ -93,10 +93,6 @@ function toClassObj(expr: string | number | { [c: string]: any }) {
for (let key in expr as any) {
const value = (expr as any)[key];
if (value) {
key = trim.call(key);
if (!key) {
continue;
}
const words = split.call(key, wordRegexp);
for (let word of words) {
result[word] = value;
@@ -143,8 +139,7 @@ export function updateClass(this: HTMLElement, val: any, oldVal: any) {
export function makePropSetter(name: string): Setter<HTMLElement> {
return function setProp(this: HTMLElement, value: any) {
// support 0, fallback to empty string for other falsy values
(this as any)[name] = value === 0 ? 0 : value || "";
(this as any)[name] = value;
};
}
@@ -5,7 +5,6 @@ type EventHandlerSetter = (this: HTMLElement, data: any) => void;
interface EventHandlerCreator {
setup: EventHandlerSetter;
update: EventHandlerSetter;
remove: (this: HTMLElement) => void;
}
export function createEventHandler(rawEvent: string): EventHandlerCreator {
@@ -39,15 +38,11 @@ function createElementHandler(evName: string, capture: boolean = false): EventHa
this.addEventListener(evName, listener, { capture });
}
function remove(this: HTMLElement) {
delete (this as any)[eventKey];
this.removeEventListener(evName, listener, { capture });
}
function update(this: HTMLElement, data: any) {
(this as any)[eventKey] = data;
}
return { setup, update, remove };
return { setup, update };
}
// Synthetic handler: a form of event delegation that allows placing only one
@@ -65,12 +60,7 @@ function createSyntheticHandler(evName: string, capture: boolean = false): Event
_data[currentId] = data;
(this as any)[eventKey] = _data;
}
function remove(this: HTMLElement) {
delete (this as any)[eventKey];
}
return { setup, update: setup, remove };
return { setup, update: setup };
}
function nativeToSyntheticEvent(eventKey: string, event: Event) {
@@ -6,7 +6,6 @@ export { list } from "./list";
export { multi } from "./multi";
export { text, comment } from "./text";
export { html } from "./html";
export { createCatcher } from "./event_catcher";
export interface VNode<T = any> {
mount(parent: HTMLElement, afterNode: Node | null): void;
+124 -186
View File
@@ -1,10 +1,4 @@
import {
compileExpr,
compileExprToArray,
interpolate,
INTERP_REGEXP,
replaceDynamicParts,
} from "./inline_expressions";
import { compileExpr, compileExprToArray, interpolate, INTERP_REGEXP } from "./inline_expressions";
import {
AST,
ASTComment,
@@ -26,8 +20,6 @@ import {
ASTTranslation,
ASTType,
ASTTPortal,
EventHandlers,
Attrs,
} from "./parser";
type BlockType = "block" | "text" | "multi" | "list" | "html" | "comment";
@@ -48,20 +40,17 @@ export interface CodeGenOptions extends Config {
const xmlDoc = document.implementation.createDocument(null, null, null);
const MODS = new Set(["stop", "capture", "prevent", "self", "synthetic"]);
let nextDataIds: { [key: string]: number } = {};
function generateId(prefix: string = "") {
nextDataIds[prefix] = (nextDataIds[prefix] || 0) + 1;
return prefix + nextDataIds[prefix];
}
// -----------------------------------------------------------------------------
// BlockDescription
// -----------------------------------------------------------------------------
class BlockDescription {
static nextBlockId = 1;
static nextDataIds: { [key: string]: number } = {};
static generateId(prefix: string) {
this.nextDataIds[prefix] = (this.nextDataIds[prefix] || 0) + 1;
return prefix + this.nextDataIds[prefix];
}
varName: string;
blockName: string;
@@ -87,7 +76,7 @@ class BlockDescription {
}
insertData(str: string, prefix: string = "d"): number {
const id = generateId(prefix);
const id = BlockDescription.generateId(prefix);
this.target.addLine(`let ${id} = ${str};`);
return this.data.push(id) - 1;
}
@@ -140,7 +129,6 @@ interface Context {
tKeyExpr: string | null;
nameSpace?: string;
tModelSelectedExpr?: string;
ctxVar?: string;
}
function createContext(parentCtx: Context, params?: Partial<Context>) {
@@ -169,11 +157,9 @@ class CodeTarget {
// maps ref name to [id, expr]
refInfo: { [name: string]: [string, string] } = {};
shouldProtectScope: boolean = false;
on: EventHandlers | null;
constructor(name: string, on?: EventHandlers | null) {
constructor(name: string) {
this.name = name;
this.on = on || null;
}
addLine(line: string, idx?: number) {
@@ -219,6 +205,7 @@ const translationRE = /^(\s*)([\s\S]+?)(\s*)$/;
export class CodeGenerator {
blocks: BlockDescription[] = [];
ids: { [key: string]: number } = {};
nextBlockId = 1;
hasSafeContext: boolean;
isDebug: boolean = false;
@@ -229,7 +216,7 @@ export class CodeGenerator {
translateFn: (s: string) => string;
translatableAttributes: string[] = TRANSLATABLE_ATTRS;
ast: AST;
staticDefs: { id: string; expr: string }[] = [];
staticCalls: { id: string; template: string }[] = [];
helpers: Set<string> = new Set();
constructor(ast: AST, options: CodeGenOptions) {
@@ -255,7 +242,7 @@ export class CodeGenerator {
const ast = this.ast;
this.isDebug = ast.type === ASTType.TDebug;
BlockDescription.nextBlockId = 1;
nextDataIds = {};
BlockDescription.nextDataIds = {};
this.compileAST(ast, {
block: null,
index: 0,
@@ -266,8 +253,7 @@ export class CodeGenerator {
});
// define blocks and utility functions
let mainCode = [
` "use strict";`,
`let { text, createBlock, list, multi, html, toggler, comment } = bdom;`,
` let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;`,
];
if (this.helpers.size) {
mainCode.push(`let { ${[...this.helpers].join(", ")} } = helpers;`);
@@ -276,8 +262,8 @@ export class CodeGenerator {
mainCode.push(`// Template name: "${this.templateName}"`);
}
for (let { id, expr } of this.staticDefs) {
mainCode.push(`const ${id} = ${expr};`);
for (let { id, template } of this.staticCalls) {
mainCode.push(`const ${id} = getTemplate(${template});`);
}
// define all blocks
@@ -286,7 +272,6 @@ export class CodeGenerator {
for (let block of this.blocks) {
if (block.dom) {
let xmlString = block.asXmlString();
xmlString = xmlString.replace(/`/g, "\\`");
if (block.dynamicTagName) {
xmlString = xmlString.replace(/^<\w+/, `<\${tag || '${block.dom.nodeName}'}`);
xmlString = xmlString.replace(/\w+>$/, `\${tag || '${block.dom.nodeName}'}>`);
@@ -318,23 +303,25 @@ export class CodeGenerator {
return code;
}
compileInNewTarget(prefix: string, ast: AST, ctx: Context, on?: EventHandlers | null): string {
const name = generateId(prefix);
compileInNewTarget(prefix: string, ast: AST, ctx: Context): string {
const name = this.generateId(prefix);
const initialTarget = this.target;
const target = new CodeTarget(name, on);
const target = new CodeTarget(name);
this.targets.push(target);
this.target = target;
this.compileAST(ast, createContext(ctx));
const subCtx: Context = createContext(ctx);
this.compileAST(ast, subCtx);
this.target = initialTarget;
return name;
}
addLine(line: string, idx?: number) {
this.target.addLine(line, idx);
addLine(line: string) {
this.target.addLine(line);
}
define(varName: string, expr: string) {
this.addLine(`const ${varName} = ${expr};`);
generateId(prefix: string = ""): string {
this.ids[prefix] = (this.ids[prefix] || 0) + 1;
return prefix + this.ids[prefix];
}
insertAnchor(block: BlockDescription) {
@@ -381,12 +368,9 @@ export class CodeGenerator {
}
if (block.isRoot && !ctx.preventRoot) {
if (this.target.on) {
blockExpr = this.wrapWithEventCatcher(blockExpr, this.target.on);
}
this.addLine(`return ${blockExpr};`);
} else {
this.define(block.varName, blockExpr);
this.addLine(`let ${block.varName} = ${blockExpr};`);
}
}
@@ -413,9 +397,9 @@ export class CodeGenerator {
.map((tok) => {
if (tok.varName && !tok.isLocal) {
if (!mapping.has(tok.varName)) {
const varId = generateId("v");
const varId = this.generateId("v");
mapping.set(tok.varName, varId);
this.define(varId, tok.value);
this.addLine(`const ${varId} = ${tok.value};`);
}
tok.value = mapping.get(tok.varName)!;
}
@@ -559,13 +543,13 @@ export class CodeGenerator {
block = this.createBlock(block, "block", ctx);
this.blocks.push(block);
if (ast.dynamicTag) {
const tagExpr = generateId("tag");
this.define(tagExpr, compileExpr(ast.dynamicTag));
const tagExpr = this.generateId("tag");
this.addLine(`let ${tagExpr} = ${compileExpr(ast.dynamicTag)};`);
block.dynamicTagName = tagExpr;
}
}
// attributes
const attrs: Attrs = {};
const attrs: { [key: string]: string } = {};
const nameSpace = ast.ns || ctx.nameSpace;
if (nameSpace && isNewBlock) {
// specific namespace uri
@@ -615,8 +599,11 @@ export class CodeGenerator {
this.target.hasRef = true;
const isDynamic = INTERP_REGEXP.test(ast.ref);
if (isDynamic) {
const str = replaceDynamicParts(ast.ref, (expr) => this.captureExpression(expr, true));
const idx = block!.insertData(`(el) => refs[${str}] = el`, "ref");
const str = ast.ref.replace(
INTERP_REGEXP,
(expr) => "${" + this.captureExpression(expr.slice(2, -2), true) + "}"
);
const idx = block!.insertData(`(el) => refs[\`${str}\`] = el`, "ref");
attrs["block-ref"] = String(idx);
} else {
let name = ast.ref;
@@ -628,7 +615,7 @@ export class CodeGenerator {
attrs["block-ref"] = String(index);
info[1] = `multiRefSetter(refs, \`${name}\`)`;
} else {
let id = generateId("ref");
let id = this.generateId("ref");
this.target.refInfo[name] = [id, `(el) => refs[\`${name}\`] = el`];
const index = block!.data.push(id) - 1;
attrs["block-ref"] = String(index);
@@ -651,12 +638,12 @@ export class CodeGenerator {
} = ast.model;
const baseExpression = compileExpr(baseExpr);
const bExprId = generateId("bExpr");
this.define(bExprId, baseExpression);
const bExprId = this.generateId("bExpr");
this.addLine(`const ${bExprId} = ${baseExpression};`);
const expression = compileExpr(expr);
const exprId = generateId("expr");
this.define(exprId, expression);
const exprId = this.generateId("expr");
this.addLine(`const ${exprId} = ${expression};`);
const fullExpression = `${bExprId}[${exprId}]`;
@@ -665,9 +652,9 @@ export class CodeGenerator {
idx = block!.insertData(`${fullExpression} === '${attrs[targetAttr]}'`, "attr");
attrs[`block-attribute-${idx}`] = specialInitTargetAttr;
} else if (hasDynamicChildren) {
const bValueId = generateId("bValue");
const bValueId = this.generateId("bValue");
tModelSelectedExpr = `${bValueId}`;
this.define(tModelSelectedExpr, fullExpression);
this.addLine(`let ${tModelSelectedExpr} = ${fullExpression}`);
} else {
idx = block!.insertData(`${fullExpression}`, "attr");
attrs[`block-attribute-${idx}`] = targetAttr;
@@ -717,13 +704,13 @@ export class CodeGenerator {
const children = block!.children.slice();
let current = children.shift();
for (let i = codeIdx; i < code.length; i++) {
if (code[i].trimStart().startsWith(`const ${current!.varName} `)) {
code[i] = code[i].replace(`const ${current!.varName}`, current!.varName);
if (code[i].trimStart().startsWith(`let ${current!.varName} `)) {
code[i] = code[i].replace(`let ${current!.varName}`, current!.varName);
current = children.shift();
if (!current) break;
}
}
this.addLine(`let ${block!.children.map((c) => c.varName)};`, codeIdx);
this.target.addLine(`let ${block!.children.map((c) => c.varName)};`, codeIdx);
}
}
}
@@ -812,13 +799,13 @@ export class CodeGenerator {
const children = block!.children.slice();
let current = children.shift();
for (let i = codeIdx; i < code.length; i++) {
if (code[i].trimStart().startsWith(`const ${current!.varName} `)) {
code[i] = code[i].replace(`const ${current!.varName}`, current!.varName);
if (code[i].trimStart().startsWith(`let ${current!.varName} `)) {
code[i] = code[i].replace(`let ${current!.varName}`, current!.varName);
current = children.shift();
if (!current) break;
}
}
this.addLine(`let ${block!.children.map((c) => c.varName)};`, codeIdx);
this.target.addLine(`let ${block!.children.map((c) => c.varName)};`, codeIdx);
}
// note: this part is duplicated from end of compilemulti:
@@ -841,10 +828,12 @@ export class CodeGenerator {
const l = `l_block${block.id}`;
const c = `c_block${block.id}`;
this.helpers.add("prepareList");
this.define(`[${keys}, ${vals}, ${l}, ${c}]`, `prepareList(${compileExpr(ast.collection)});`);
this.addLine(
`const [${keys}, ${vals}, ${l}, ${c}] = prepareList(${compileExpr(ast.collection)});`
);
// Throw errors on duplicate keys in dev mode
if (this.dev) {
this.define(`keys${block.id}`, `new Set()`);
this.addLine(`const keys${block.id} = new Set();`);
}
this.addLine(`for (let ${loopVar} = 0; ${loopVar} < ${l}; ${loopVar}++) {`);
this.target.indentLevel++;
@@ -861,7 +850,7 @@ export class CodeGenerator {
if (!ast.hasNoValue) {
this.addLine(`ctx[\`${ast.elem}_value\`] = ${keys}[${loopVar}];`);
}
this.define(`key${this.target.loopLevel}`, ast.key ? compileExpr(ast.key) : loopVar);
this.addLine(`let key${this.target.loopLevel} = ${ast.key ? compileExpr(ast.key) : loopVar};`);
if (this.dev) {
// Throw error on duplicate keys in dev mode
this.addLine(
@@ -872,9 +861,9 @@ export class CodeGenerator {
let id: string;
if (ast.memo) {
this.target.hasCache = true;
id = generateId();
this.define(`memo${id}`, compileExpr(ast.memo));
this.define(`vnode${id}`, `cache[key${this.target.loopLevel}];`);
id = this.generateId();
this.addLine(`let memo${id} = ${compileExpr(ast.memo)}`);
this.addLine(`let vnode${id} = cache[key${this.target.loopLevel}];`);
this.addLine(`if (vnode${id}) {`);
this.target.indentLevel++;
this.addLine(`if (shallowEqual(vnode${id}.memo, memo${id})) {`);
@@ -907,8 +896,8 @@ export class CodeGenerator {
}
compileTKey(ast: ASTTKey, ctx: Context) {
const tKeyExpr = generateId("tKey_");
this.define(tKeyExpr, compileExpr(ast.expr));
const tKeyExpr = this.generateId("tKey_");
this.addLine(`const ${tKeyExpr} = ${compileExpr(ast.expr)};`);
ctx = createContext(ctx, {
tKeyExpr,
block: ctx.block,
@@ -954,13 +943,13 @@ export class CodeGenerator {
const children = block!.children.slice();
let current = children.shift();
for (let i = codeIdx; i < code.length; i++) {
if (code[i].trimStart().startsWith(`const ${current!.varName} `)) {
code[i] = code[i].replace(`const ${current!.varName}`, current!.varName);
if (code[i].trimStart().startsWith(`let ${current!.varName} `)) {
code[i] = code[i].replace(`let ${current!.varName}`, current!.varName);
current = children.shift();
if (!current) break;
}
}
this.addLine(`let ${block!.children.map((c) => c.varName)};`, codeIdx);
this.target.addLine(`let ${block!.children.map((c) => c.varName)};`, codeIdx);
}
}
@@ -971,21 +960,16 @@ export class CodeGenerator {
compileTCall(ast: ASTTCall, ctx: Context) {
let { block, forceNewBlock } = ctx;
let ctxVar = ctx.ctxVar || "ctx";
if (ast.context) {
ctxVar = generateId("ctx");
this.addLine(`let ${ctxVar} = ${compileExpr(ast.context)};`);
}
if (ast.body) {
this.addLine(`${ctxVar} = Object.create(${ctxVar});`);
this.addLine(`${ctxVar}[isBoundary] = 1;`);
this.addLine(`ctx = Object.create(ctx);`);
this.addLine(`ctx[isBoundary] = 1;`);
this.helpers.add("isBoundary");
const nextId = BlockDescription.nextBlockId;
const subCtx: Context = createContext(ctx, { preventRoot: true, ctxVar });
const subCtx: Context = createContext(ctx, { preventRoot: true });
this.compileAST({ type: ASTType.Multi, content: ast.body }, subCtx);
if (nextId !== BlockDescription.nextBlockId) {
this.helpers.add("zero");
this.addLine(`${ctxVar}[zero] = b${nextId};`);
this.addLine(`ctx[zero] = b${nextId};`);
}
}
const isDynamic = INTERP_REGEXP.test(ast.name);
@@ -997,27 +981,26 @@ export class CodeGenerator {
}
const key = `key + \`${this.generateComponentKey()}\``;
if (isDynamic) {
const templateVar = generateId("template");
if (!this.staticDefs.find((d) => d.id === "call")) {
this.staticDefs.push({ id: "call", expr: `app.callTemplate.bind(app)` });
}
this.define(templateVar, subTemplate);
const templateVar = this.generateId("template");
this.addLine(`const ${templateVar} = ${subTemplate};`);
block = this.createBlock(block, "multi", ctx);
this.insertBlock(`call(this, ${templateVar}, ${ctxVar}, node, ${key})`, block!, {
this.helpers.add("call");
this.insertBlock(`call(this, ${templateVar}, ctx, node, ${key})`, block!, {
...ctx,
forceNewBlock: !block,
});
} else {
const id = generateId(`callTemplate_`);
this.staticDefs.push({ id, expr: `app.getTemplate(${subTemplate})` });
const id = this.generateId(`callTemplate_`);
this.helpers.add("getTemplate");
this.staticCalls.push({ id, template: subTemplate });
block = this.createBlock(block, "multi", ctx);
this.insertBlock(`${id}.call(this, ${ctxVar}, node, ${key})`, block!, {
this.insertBlock(`${id}.call(this, ctx, node, ${key})`, block!, {
...ctx,
forceNewBlock: !block,
});
}
if (ast.body && !ctx.isLast) {
this.addLine(`${ctxVar} = ${ctxVar}.__proto__;`);
this.addLine(`ctx = ctx.__proto__;`);
}
}
@@ -1040,7 +1023,7 @@ export class CodeGenerator {
this.helpers.add("LazyValue");
const bodyAst: AST = { type: ASTType.Multi, content: ast.body };
const name = this.compileInNewTarget("value", bodyAst, ctx);
let value = `new LazyValue(${name}, ctx, this, node)`;
let value = `new LazyValue(${name}, ctx, node)`;
value = ast.value ? (value ? `withDefault(${expr}, ${value})` : expr) : value;
this.addLine(`ctx[\`${ast.name}\`] = ${value};`);
} else {
@@ -1055,12 +1038,12 @@ export class CodeGenerator {
value = expr;
}
this.helpers.add("setContextValue");
this.addLine(`setContextValue(${ctx.ctxVar || "ctx"}, "${ast.name}", ${value});`);
this.addLine(`setContextValue(ctx, "${ast.name}", ${value});`);
}
}
generateComponentKey() {
const parts = [generateId("__")];
const parts = [this.generateId("__")];
for (let i = 0; i < this.target.loopLevel; i++) {
parts.push(`\${key${i + 1}}`);
}
@@ -1094,49 +1077,46 @@ export class CodeGenerator {
return `${name}: ${value || undefined}`;
}
formatPropObject(obj: { [prop: string]: any }): string[] {
return Object.entries(obj).map(([k, v]) => this.formatProp(k, v));
}
getPropString(props: string[], dynProps: string | null): string {
let propString = `{${props.join(",")}}`;
if (dynProps) {
propString = `Object.assign({}, ${compileExpr(dynProps)}${
props.length ? ", " + propString : ""
})`;
formatPropObject(obj: { [prop: string]: any }): string {
const params = [];
for (const [n, v] of Object.entries(obj)) {
params.push(this.formatProp(n, v));
}
return propString;
return params.join(", ");
}
compileComponent(ast: ASTComponent, ctx: Context) {
let { block } = ctx;
// props
const hasSlotsProp = "slots" in (ast.props || {});
const props: string[] = ast.props ? this.formatPropObject(ast.props) : [];
const hasSlotsProp = "slots" in ast.props;
const props: string[] = [];
const propExpr = this.formatPropObject(ast.props);
if (propExpr) {
props.push(propExpr);
}
// slots
const hasSlot = !!Object.keys(ast.slots).length;
let slotDef: string = "";
if (ast.slots) {
if (hasSlot) {
let ctxStr = "ctx";
if (this.target.loopLevel || !this.hasSafeContext) {
ctxStr = generateId("ctx");
ctxStr = this.generateId("ctx");
this.helpers.add("capture");
this.define(ctxStr, `capture(ctx)`);
this.addLine(`const ${ctxStr} = capture(ctx);`);
}
let slotStr: string[] = [];
for (let slotName in ast.slots) {
const slotAst = ast.slots[slotName];
const params = [];
if (slotAst.content) {
const name = this.compileInNewTarget("slot", slotAst.content, ctx, slotAst.on);
params.push(`__render: ${name}, __ctx: ${ctxStr}`);
}
const slotAst = ast.slots[slotName].content;
const name = this.compileInNewTarget("slot", slotAst, ctx);
const params = [`__render: ${name}, __ctx: ${ctxStr}`];
const scope = ast.slots[slotName].scope;
if (scope) {
params.push(`__scope: "${scope}"`);
}
if (ast.slots[slotName].attrs) {
params.push(...this.formatPropObject(ast.slots[slotName].attrs!));
params.push(this.formatPropObject(ast.slots[slotName].attrs!));
}
const slotInfo = `{${params.join(", ")}}`;
slotStr.push(`'${slotName}': ${slotInfo}`);
@@ -1145,30 +1125,37 @@ export class CodeGenerator {
}
if (slotDef && !(ast.dynamicProps || hasSlotsProp)) {
this.helpers.add("markRaw");
props.push(`slots: markRaw(${slotDef})`);
props.push(`slots: ${slotDef}`);
}
let propString = this.getPropString(props, ast.dynamicProps);
const propStr = `{${props.join(",")}}`;
let propString = propStr;
if (ast.dynamicProps) {
if (!props.length) {
propString = `Object.assign({}, ${compileExpr(ast.dynamicProps)})`;
} else {
propString = `Object.assign({}, ${compileExpr(ast.dynamicProps)}, ${propStr})`;
}
}
let propVar: string;
if ((slotDef && (ast.dynamicProps || hasSlotsProp)) || this.dev) {
propVar = generateId("props");
this.define(propVar!, propString);
propVar = this.generateId("props");
this.addLine(`const ${propVar!} = ${propString};`);
propString = propVar!;
}
if (slotDef && (ast.dynamicProps || hasSlotsProp)) {
this.helpers.add("markRaw");
this.addLine(`${propVar!}.slots = markRaw(Object.assign(${slotDef}, ${propVar!}.slots))`);
this.addLine(`${propVar!}.slots = Object.assign(${slotDef}, ${propVar!}.slots)`);
}
// cmap key
const key = this.generateComponentKey();
let expr: string;
if (ast.isDynamic) {
expr = generateId("Comp");
this.define(expr, compileExpr(ast.name));
expr = this.generateId("Comp");
this.addLine(`let ${expr} = ${compileExpr(ast.name)};`);
} else {
expr = `\`${ast.name}\``;
}
@@ -1186,46 +1173,15 @@ export class CodeGenerator {
if (ctx.tKeyExpr) {
keyArg = `${ctx.tKeyExpr} + ${keyArg}`;
}
let id = generateId("comp");
this.staticDefs.push({
id,
expr: `app.createComponent(${
ast.isDynamic ? null : expr
}, ${!ast.isDynamic}, ${!!ast.slots}, ${!!ast.dynamicProps}, ${
!ast.props && !ast.dynamicProps
})`,
});
let blockExpr = `${id}(${propString}, ${keyArg}, node, this, ${ast.isDynamic ? expr : null})`;
const blockArgs = `${expr}, ${propString}, ${keyArg}, node, ctx`;
let blockExpr = `component(${blockArgs})`;
if (ast.isDynamic) {
blockExpr = `toggler(${expr}, ${blockExpr})`;
}
// event handling
if (ast.on) {
blockExpr = this.wrapWithEventCatcher(blockExpr, ast.on);
}
block = this.createBlock(block, "multi", ctx);
this.insertBlock(blockExpr, block, ctx);
}
wrapWithEventCatcher(expr: string, on: EventHandlers): string {
this.helpers.add("createCatcher");
let name = generateId("catcher");
let spec: any = {};
let handlers: any[] = [];
for (let ev in on) {
let handlerId = generateId("hdlr");
let idx = handlers.push(handlerId) - 1;
spec[ev] = idx;
const handler = this.generateHandlerCode(ev, on[ev]);
this.define(handlerId, handler);
}
this.staticDefs.push({ id: name, expr: `createCatcher(${JSON.stringify(spec)})` });
return `${name}(${expr}, [${handlers.join(",")}])`;
}
compileTSlot(ast: ASTSlot, ctx: Context) {
this.helpers.add("callSlot");
let { block } = ctx;
@@ -1238,29 +1194,20 @@ export class CodeGenerator {
} else {
slotName = "'" + ast.name + "'";
}
const dynProps = ast.attrs ? ast.attrs["t-props"] : null;
if (ast.attrs) {
delete ast.attrs["t-props"];
}
const props = ast.attrs ? this.formatPropObject(ast.attrs) : [];
const scope = this.getPropString(props, dynProps);
const scope = ast.attrs ? `{${this.formatPropObject(ast.attrs)}}` : null;
if (ast.defaultContent) {
const name = this.compileInNewTarget("defaultContent", ast.defaultContent, ctx);
blockString = `callSlot(ctx, node, key, ${slotName}, ${dynamic}, ${scope}, ${name})`;
} else {
if (dynamic) {
let name = generateId("slot");
this.define(name, slotName);
blockString = `toggler(${name}, callSlot(ctx, node, key, ${name}, ${dynamic}, ${scope}))`;
let name = this.generateId("slot");
this.addLine(`const ${name} = ${slotName};`);
blockString = `toggler(${name}, callSlot(ctx, node, key, ${name}), ${dynamic}, ${scope})`;
} else {
blockString = `callSlot(ctx, node, key, ${slotName}, ${dynamic}, ${scope})`;
}
}
// event handling
if (ast.on) {
blockString = this.wrapWithEventCatcher(blockString, ast.on);
}
if (block) {
this.insertAnchor(block);
}
@@ -1274,27 +1221,18 @@ export class CodeGenerator {
}
}
compileTPortal(ast: ASTTPortal, ctx: Context) {
if (!this.staticDefs.find((d) => d.id === "Portal")) {
this.staticDefs.push({ id: "Portal", expr: `app.Portal` });
}
this.helpers.add("Portal");
let { block } = ctx;
const name = this.compileInNewTarget("slot", ast.content, ctx);
const key = this.generateComponentKey();
let ctxStr = "ctx";
if (this.target.loopLevel || !this.hasSafeContext) {
ctxStr = generateId("ctx");
ctxStr = this.generateId("ctx");
this.helpers.add("capture");
this.define(ctxStr, `capture(ctx)`);
this.addLine(`const ${ctxStr} = capture(ctx);`);
}
let id = generateId("comp");
this.staticDefs.push({
id,
expr: `app.createComponent(null, false, true, false, false)`,
});
const target = compileExpr(ast.target);
const blockString = `${id}({target: ${target},slots: {'default': {__render: ${name}, __ctx: ${ctxStr}}}}, key + \`${key}\`, node, ctx, Portal)`;
const blockString = `component(Portal, {target: ${ast.target},slots: {'default': {__render: ${name}, __ctx: ${ctxStr}}}}, key + \`${key}\`, node, ctx)`;
if (block) {
this.insertAnchor(block);
}
+3 -4
View File
@@ -1,11 +1,10 @@
import type { TemplateSet } from "../runtime/template_set";
import type { BDom } from "../runtime/blockdom";
import type { BDom } from "../blockdom";
import { CodeGenerator, Config } from "./code_generator";
import { parse } from "./parser";
export type Template = (context: any, vnode: any, key?: string) => BDom;
export type TemplateFunction = (app: TemplateSet, bdom: any, helpers: any) => Template;
export type TemplateFunction = (blocks: any, utils: any) => Template;
interface CompileOptions extends Config {
name?: string;
@@ -27,5 +26,5 @@ export function compile(
const codeGenerator = new CodeGenerator(ast, { ...options, hasSafeContext });
const code = codeGenerator.generateCode();
// template function
return new Function("app, bdom, helpers", code) as TemplateFunction;
return new Function("bdom, helpers", code) as TemplateFunction;
}
+21 -36
View File
@@ -85,9 +85,8 @@ const STATIC_TOKEN_MAP: { [key: string]: TKind } = Object.assign(Object.create(n
});
// note that the space after typeof is relevant. It makes sure that the formatted
// expression has a space after typeof. Currently we don't support delete and void
const OPERATORS =
"...,.,===,==,+,!==,!=,!,||,&&,>=,>,<=,<,?,-,*,/,%,typeof ,=>,=,;,in ,new ,|,&,^,~".split(",");
// expression has a space after typeof
const OPERATORS = "...,.,===,==,+,!==,!=,!,||,&&,>=,>,<=,<,?,-,*,/,%,typeof ,=>,=,;,in ".split(",");
type Tokenizer = (expr: string) => Token | false;
@@ -200,30 +199,24 @@ const TOKENIZERS = [
export function tokenize(expr: string): Token[] {
const result: Token[] = [];
let token: boolean | Token = true;
let error: any;
let current = expr;
try {
while (token) {
current = current.trim();
if (current) {
for (let tokenizer of TOKENIZERS) {
token = tokenizer(current);
if (token) {
result.push(token);
current = current.slice(token.size || token.value.length);
break;
}
while (token) {
expr = expr.trim();
if (expr) {
for (let tokenizer of TOKENIZERS) {
token = tokenizer(expr);
if (token) {
result.push(token);
expr = expr.slice(token.size || token.value.length);
break;
}
} else {
token = false;
}
} else {
token = false;
}
} catch (e) {
error = e; // Silence all errors and throw a generic error below
}
if (current.length || error) {
throw new Error(`Tokenizer error: could not tokenize \`${expr}\``);
if (expr.length) {
throw new Error(`Tokenizer error: could not tokenize "${expr}"`);
}
return result;
}
@@ -345,29 +338,21 @@ export function compileExprToArray(expr: string): Token[] {
return tokens;
}
// Leading spaces are trimmed during tokenization, so they need to be added back for some values
const paddedValues = new Map([["in ", " in "]]);
export function compileExpr(expr: string): string {
return compileExprToArray(expr)
.map((t) => paddedValues.get(t.value) || t.value)
.map((t) => t.value)
.join("");
}
export const INTERP_REGEXP = /\{\{.*?\}\}|\#\{.*?\}/g;
export const INTERP_REGEXP = /\{\{.*?\}\}/g;
const INTERP_GROUP_REGEXP = /\{\{.*?\}\}/g;
export function replaceDynamicParts(s: string, replacer: (s: string) => string) {
export function interpolate(s: string): string {
let matches = s.match(INTERP_REGEXP);
if (matches && matches[0].length === s.length) {
return `(${replacer(s.slice(2, matches[0][0] === "{" ? -2 : -1))})`;
return `(${compileExpr(s.slice(2, -2))})`;
}
let r = s.replace(
INTERP_REGEXP,
(s) => "${" + replacer(s.slice(2, s[0] === "{" ? -2 : -1)) + "}"
);
let r = s.replace(INTERP_GROUP_REGEXP, (s) => "${" + compileExpr(s.slice(2, -2)) + "}");
return "`" + r + "`";
}
export function interpolate(s: string): string {
return replaceDynamicParts(s, compileExpr);
}
+41 -79
View File
@@ -2,9 +2,6 @@
// AST Type definition
// -----------------------------------------------------------------------------
export type EventHandlers = { [eventName: string]: string };
export type Attrs = { [attrs: string]: string };
export const enum ASTType {
Text,
Comment,
@@ -37,25 +34,25 @@ export interface ASTComment {
}
interface TModelInfo {
hasDynamicChildren?: boolean;
baseExpr: string;
expr: string;
targetAttr: string;
specialInitTargetAttr: string | null;
eventType: "change" | "click" | "input";
shouldTrim: boolean;
shouldNumberize: boolean;
hasDynamicChildren: boolean;
specialInitTargetAttr: string | null;
}
export interface ASTDomNode {
type: ASTType.DomNode;
tag: string;
content: AST[];
attrs: Attrs | null;
ref: string | null;
on: EventHandlers | null;
model: TModelInfo | null;
dynamicTag: string | null;
attrs: { [key: string]: string };
content: AST[];
ref: string | null;
on: { [key: string]: string };
model?: TModelInfo | null;
ns: string | null;
}
@@ -96,13 +93,13 @@ export interface ASTTForEach {
type: ASTType.TForEach;
collection: string;
elem: string;
key: string | null;
body: AST;
memo: string;
hasNoFirst: boolean;
hasNoLast: boolean;
hasNoIndex: boolean;
hasNoValue: boolean;
key: string | null;
}
export interface ASTTKey {
@@ -115,14 +112,6 @@ export interface ASTTCall {
type: ASTType.TCall;
name: string;
body: AST[] | null;
context: string | null;
}
interface SlotDefinition {
content: AST | null;
scope: string | null;
on: EventHandlers | null;
attrs: Attrs | null;
}
export interface ASTComponent {
@@ -130,16 +119,14 @@ export interface ASTComponent {
name: string;
isDynamic: boolean;
dynamicProps: string | null;
on: EventHandlers | null;
props: { [name: string]: string } | null;
slots: { [name: string]: SlotDefinition } | null;
props: { [name: string]: string };
slots: { [name: string]: { content: AST; attrs?: { [key: string]: string }; scope?: string } };
}
export interface ASTSlot {
type: ASTType.TSlot;
name: string;
attrs: Attrs | null;
on: EventHandlers | null;
attrs: { [key: string]: string };
defaultContent: AST | null;
}
@@ -318,9 +305,6 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
if (tagName === "t" && !dynamicTag) {
return null;
}
if (tagName.startsWith("block-")) {
throw new Error(`Invalid tag name: '${tagName}'`);
}
ctx = Object.assign({}, ctx);
if (tagName === "pre") {
ctx.inPreTag = true;
@@ -332,8 +316,8 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
node.removeAttribute("t-ref");
const nodeAttrsNames = node.getAttributeNames();
let attrs: ASTDomNode["attrs"] = null;
let on: EventHandlers | null = null;
const attrs: ASTDomNode["attrs"] = {};
const on: ASTDomNode["on"] = {};
let model: TModelInfo | null = null;
for (let attr of nodeAttrsNames) {
@@ -342,7 +326,6 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
if (attr === "t-on") {
throw new Error("Missing event name with t-on directive");
}
on = on || {};
on[attr.slice(5)] = value;
} else if (attr.startsWith("t-model")) {
if (!["input", "select", "textarea"].includes(tagName)) {
@@ -380,7 +363,6 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
targetAttr: isCheckboxInput ? "checked" : "value",
specialInitTargetAttr: isRadioInput ? "checked" : null,
eventType,
hasDynamicChildren: false,
shouldTrim: hasTrimMod && (isOtherInput || isTextarea),
shouldNumberize: hasNumberMod && (isOtherInput || isTextarea),
};
@@ -389,8 +371,6 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
ctx = Object.assign({}, ctx);
ctx.tModelInfo = model;
}
} else if (attr.startsWith("block-")) {
throw new Error(`Invalid attribute: '${attr}'`);
} else if (attr !== "t-name") {
if (attr.startsWith("t-") && !attr.startsWith("t-att")) {
throw new Error(`Unknown QWeb directive: '${attr}'`);
@@ -399,7 +379,6 @@ function parseDOMNode(node: Element, ctx: ParsingContext): AST | null {
if (tModel && ["t-att-value", "t-attf-value"].includes(attr)) {
tModel.hasDynamicChildren = true;
}
attrs = attrs || {};
attrs[attr] = value;
}
}
@@ -558,13 +537,11 @@ function parseTCall(node: Element, ctx: ParsingContext): AST | null {
return null;
}
const subTemplate = node.getAttribute("t-call")!;
const context = node.getAttribute("t-call-context");
node.removeAttribute("t-call");
node.removeAttribute("t-call-context");
node.removeAttribute("t-call");
if (node.tagName !== "t") {
const ast = parseNode(node, ctx);
const tcall: AST = { type: ASTType.TCall, name: subTemplate, body: null, context };
const tcall: AST = { type: ASTType.TCall, name: subTemplate, body: null };
if (ast && ast.type === ASTType.DomNode) {
ast.content = [tcall];
return ast;
@@ -572,7 +549,7 @@ function parseTCall(node: Element, ctx: ParsingContext): AST | null {
if (ast && ast.type === ASTType.TComponent) {
return {
...ast,
slots: { default: { content: tcall, scope: null, on: null, attrs: null } },
slots: { default: { content: tcall } },
};
}
}
@@ -582,7 +559,6 @@ function parseTCall(node: Element, ctx: ParsingContext): AST | null {
type: ASTType.TCall,
name: subTemplate,
body: body.length ? body : null,
context,
};
}
@@ -669,6 +645,7 @@ function parseTSetNode(node: Element, ctx: ParsingContext): AST | null {
// Error messages when trying to use an unsupported directive on a component
const directiveErrorMap = new Map([
["t-on", "t-on is no longer supported on components. Consider passing a callback in props."],
[
"t-ref",
"t-ref is no longer supported on components. Consider exposing only the public part of the component's API through a callback prop.",
@@ -702,26 +679,19 @@ function parseComponent(node: Element, ctx: ParsingContext): AST | null {
const defaultSlotScope = node.getAttribute("t-slot-scope");
node.removeAttribute("t-slot-scope");
let on: ASTComponent["on"] = null;
let props: ASTComponent["props"] = null;
const props: ASTComponent["props"] = {};
for (let name of node.getAttributeNames()) {
const value = node.getAttribute(name)!;
if (name.startsWith("t-")) {
if (name.startsWith("t-on-")) {
on = on || {};
on[name.slice(5)] = value;
} else {
const message = directiveErrorMap.get(name.split("-").slice(0, 2).join("-"));
throw new Error(message || `unsupported directive on Component: ${name}`);
}
const message = directiveErrorMap.get(name.split("-").slice(0, 2).join("-"));
throw new Error(message || `unsupported directive on Component: ${name}`);
} else {
props = props || {};
props[name] = value;
}
}
let slots: ASTComponent["slots"] | null = null;
const slots: ASTComponent["slots"] = {};
if (node.hasChildNodes()) {
const clone = <Element>node.cloneNode(true);
@@ -753,34 +723,34 @@ function parseComponent(node: Element, ctx: ParsingContext): AST | null {
slotNode.removeAttribute("t-set-slot");
slotNode.remove();
const slotAst = parseNode(slotNode, ctx);
let on: SlotDefinition["on"] = null;
let attrs: Attrs | null = null;
let scope: string | null = null;
for (let attributeName of slotNode.getAttributeNames()) {
const value = slotNode.getAttribute(attributeName)!;
if (attributeName === "t-slot-scope") {
scope = value;
continue;
} else if (attributeName.startsWith("t-on-")) {
on = on || {};
on[attributeName.slice(5)] = value;
} else {
attrs = attrs || {};
if (slotAst) {
const slotInfo: any = { content: slotAst };
const attrs: { [key: string]: string } = {};
for (let attributeName of slotNode.getAttributeNames()) {
const value = slotNode.getAttribute(attributeName)!;
if (attributeName === "t-slot-scope") {
slotInfo.scope = value;
continue;
}
attrs[attributeName] = value;
}
if (Object.keys(attrs).length) {
slotInfo.attrs = attrs;
}
slots[name] = slotInfo;
}
slots = slots || {};
slots[name] = { content: slotAst, on, attrs, scope };
}
// default slot
const defaultContent = parseChildNodes(clone, ctx);
if (defaultContent) {
slots = slots || {};
slots.default = { content: defaultContent, on, attrs: null, scope: defaultSlotScope };
slots.default = { content: defaultContent };
if (defaultSlotScope) {
slots.default.scope = defaultSlotScope;
}
}
}
return { type: ASTType.TComponent, name, isDynamic, dynamicProps, props, slots, on };
return { type: ASTType.TComponent, name, isDynamic, dynamicProps, props, slots };
}
// -----------------------------------------------------------------------------
@@ -793,23 +763,15 @@ function parseTSlot(node: Element, ctx: ParsingContext): AST | null {
}
const name = node.getAttribute("t-slot")!;
node.removeAttribute("t-slot");
let attrs: Attrs | null = null;
let on: ASTComponent["on"] = null;
const attrs: { [key: string]: string } = {};
for (let attributeName of node.getAttributeNames()) {
const value = node.getAttribute(attributeName)!;
if (attributeName.startsWith("t-on-")) {
on = on || {};
on[attributeName.slice(5)] = value;
} else {
attrs = attrs || {};
attrs[attributeName] = value;
}
attrs[attributeName] = value;
}
return {
type: ASTType.TSlot,
name,
attrs,
on,
defaultContent: parseChildNodes(node, ctx),
};
}
@@ -1,17 +1,15 @@
import { Schema } from "./validation";
import type { ComponentNode } from "./component_node";
// -----------------------------------------------------------------------------
// Component Class
// -----------------------------------------------------------------------------
export type Props = { [key: string]: any };
type Props = { [key: string]: any };
interface StaticComponentProperties {
template: string;
defaultProps?: any;
props?: Schema;
components?: { [componentName: string]: ComponentConstructor };
props?: any;
}
export type ComponentConstructor<P extends Props = any, E = any> = (new (
@@ -38,7 +36,7 @@ export class Component<Props = any, Env = any> {
setup() {}
render(deep: boolean = false) {
this.__owl__.render(deep === true);
render() {
this.__owl__.render();
}
}
@@ -1,18 +1,17 @@
import type { App, Env } from "./app";
import { BDom, VNode } from "./blockdom";
import { Component, ComponentConstructor, Props } from "./component";
import { fibersInError, handleError } from "./error_handling";
import { Fiber, makeChildFiber, makeRootFiber, MountFiber, MountOptions } from "./fibers";
import type { App, Env } from "../app/app";
import { BDom, VNode } from "../blockdom";
import { Component, ComponentConstructor } from "./component";
import {
clearReactivesForCallback,
getSubscriptions,
NonReactive,
Reactive,
reactive,
TARGET,
} from "./reactivity";
Fiber,
makeChildFiber,
makeRootFiber,
MountFiber,
MountOptions,
RootFiber,
} from "./fibers";
import { handleError, fibersInError } from "./error_handling";
import { applyDefaultProps } from "./props_validation";
import { STATUS } from "./status";
import { batched, Callback } from "./utils";
let currentNode: ComponentNode | null = null;
@@ -27,59 +26,64 @@ export function useComponent(): Component {
return currentNode!.component;
}
/**
* Apply default props (only top level).
*/
function applyDefaultProps<P extends object>(props: P, defaultProps: Partial<P>) {
for (let propName in defaultProps) {
if (props[propName] === undefined) {
(props as any)[propName] = defaultProps[propName];
export function component(
name: string | typeof Component,
props: any,
key: string,
ctx: ComponentNode,
parent: any
): ComponentNode {
let node: any = ctx.children[key];
let isDynamic = typeof name !== "string";
if (node) {
if (node.status < STATUS.MOUNTED) {
node.destroy();
node = undefined;
} else if (node.status === STATUS.DESTROYED) {
node = undefined;
}
}
}
// -----------------------------------------------------------------------------
// Integration with reactivity system (useState)
// -----------------------------------------------------------------------------
const batchedRenderFunctions = new WeakMap<ComponentNode, Callback>();
/**
* Creates a reactive object that will be observed by the current component.
* Reading data from the returned object (eg during rendering) will cause the
* component to subscribe to that data and be rerendered when it changes.
*
* @param state the state to observe
* @returns a reactive object that will cause the component to re-render on
* relevant changes
* @see reactive
*/
export function useState<T extends object>(state: T): Reactive<T> | NonReactive<T> {
const node = getCurrent();
let render = batchedRenderFunctions.get(node)!;
if (!render) {
render = batched(node.render.bind(node, false));
batchedRenderFunctions.set(node, render);
// manual implementation of onWillDestroy to break cyclic dependency
node.willDestroy.push(clearReactivesForCallback.bind(null, render));
if (isDynamic && node && node.component.constructor !== name) {
node = undefined;
}
return reactive(state, render);
const parentFiber = ctx.fiber!;
if (node) {
node.updateAndRender(props, parentFiber);
} else {
// new component
let C;
if (isDynamic) {
C = name;
} else {
C = parent.constructor.components[name as any];
if (!C) {
throw new Error(`Cannot find the definition of component "${name}"`);
}
}
node = new ComponentNode(C, props, ctx.app, ctx);
ctx.children[key] = node;
const fiber = makeChildFiber(node, parentFiber);
node.initiateRender(fiber);
}
return node;
}
// -----------------------------------------------------------------------------
// Component VNode class
// Component VNode
// -----------------------------------------------------------------------------
type LifecycleHook = Function;
export class ComponentNode<P extends Props = any, E = any> implements VNode<ComponentNode<P, E>> {
export class ComponentNode<P = any, E = any> implements VNode<ComponentNode<P, E>> {
el?: HTMLElement | Text | undefined;
app: App;
fiber: Fiber | null = null;
component: Component<P, E>;
bdom: BDom | null = null;
status: STATUS = STATUS.NEW;
forceNextRender: boolean = false;
parentKey: string | null;
props: P;
renderFn: Function;
parent: ComponentNode | null;
@@ -96,33 +100,15 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
patched: LifecycleHook[] = [];
willDestroy: LifecycleHook[] = [];
constructor(
C: ComponentConstructor<P, E>,
props: P,
app: App,
parent: ComponentNode | null,
parentKey: string | null
) {
constructor(C: ComponentConstructor<P, E>, props: P, app: App, parent?: ComponentNode) {
currentNode = this;
this.app = app;
this.parent = parent;
this.props = props;
this.parentKey = parentKey;
this.parent = parent || null;
this.level = parent ? parent.level + 1 : 0;
const defaultProps = C.defaultProps;
props = Object.assign({}, props);
if (defaultProps) {
applyDefaultProps(props, defaultProps);
}
applyDefaultProps(props, C);
const env = (parent && parent.childEnv) || app.env;
this.childEnv = env;
for (const key in props) {
const prop = props[key];
if (prop && typeof prop === "object" && prop[TARGET]) {
props[key] = useState(prop);
}
}
this.component = new C(props, env, this);
this.component = new C(props, env, this) as any;
this.renderFn = app.getTemplate(C.template).bind(this.component, this.component, this);
this.component.setup();
currentNode = null;
@@ -147,35 +133,26 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
return;
}
if (this.status === STATUS.NEW && this.fiber === fiber) {
fiber.render();
this._render(fiber);
}
}
async render(deep: boolean) {
async render() {
let current = this.fiber;
if (current && (current.root!.locked || (current as any).bdom === true)) {
if (current && current.root!.locked) {
await Promise.resolve();
// situation may have changed after the microtask tick
current = this.fiber;
}
if (current) {
if (!current.bdom && !fibersInError.has(current)) {
if (deep) {
// we want the render from this point on to be with deep=true
current.deep = deep;
}
return;
}
// if current rendering was with deep=true, we want this one to be the same
deep = deep || current.deep;
} else if (!this.bdom) {
if (current && !current.bdom && !fibersInError.has(current)) {
return;
}
if (!this.bdom && !current) {
return;
}
const fiber = makeRootFiber(this);
fiber.deep = deep;
this.fiber = fiber;
this.app.scheduler.addFiber(fiber);
await Promise.resolve();
if (this.status === STATUS.DESTROYED) {
@@ -193,7 +170,16 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
// embedded in a rendering coming from above, so the fiber will be rendered
// in the next microtick anyway, so we should not render it again.
if (this.fiber === fiber && (current || !fiber.parent)) {
fiber.render();
this._render(fiber);
}
}
_render(fiber: Fiber | RootFiber) {
try {
fiber.bdom = this.renderFn();
fiber.root!.counter--;
} catch (e) {
handleError({ node: this, error: e });
}
}
@@ -215,46 +201,25 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
for (let child of Object.values(this.children)) {
child._destroy();
}
if (this.willDestroy.length) {
try {
for (let cb of this.willDestroy) {
cb.call(component);
}
} catch (e) {
handleError({ error: e, node: this });
}
for (let cb of this.willDestroy) {
cb.call(component);
}
this.status = STATUS.DESTROYED;
}
async updateAndRender(props: P, parentFiber: Fiber) {
const rawProps = props;
props = Object.assign({}, props);
async updateAndRender(props: any, parentFiber: Fiber) {
// update
const fiber = makeChildFiber(this, parentFiber);
this.fiber = fiber;
const component = this.component;
const defaultProps = (component.constructor as any).defaultProps;
if (defaultProps) {
applyDefaultProps(props, defaultProps);
}
currentNode = this;
for (const key in props) {
const prop = props[key];
if (prop && typeof prop === "object" && prop[TARGET]) {
props[key] = useState(prop);
}
}
currentNode = null;
applyDefaultProps(props, component.constructor as any);
const prom = Promise.all(this.willUpdateProps.map((f) => f.call(component, props)));
await prom;
if (fiber !== this.fiber) {
return;
}
component.props = props;
this.props = rawProps;
fiber.render();
this._render(fiber);
const parentRoot = parentFiber.root!;
if (this.willPatch.length) {
parentRoot.willPatch.push(fiber);
@@ -304,7 +269,6 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
bdom.mount(parent, anchor);
this.status = STATUS.MOUNTED;
this.fiber!.appliedToDom = true;
this.children = this.fiber!.childrenMap;
this.fiber = null;
}
@@ -313,23 +277,12 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
}
patch() {
if (this.fiber && this.fiber.parent) {
// we only patch here renderings coming from above. renderings initiated
// by the component will be patched independently in the appropriate
// fiber.complete
this._patch();
const hasChildren = Object.keys(this.children).length > 0;
this.bdom!.patch(this!.fiber!.bdom!, hasChildren);
if (hasChildren) {
this.cleanOutdatedChildren();
}
}
_patch() {
let hasChildren = false;
for (let _k in this.children) {
hasChildren = true;
break;
}
const fiber = this.fiber!;
this.children = fiber.childrenMap;
this.bdom!.patch(fiber.bdom!, hasChildren);
fiber.appliedToDom = true;
this.fiber!.appliedToDom = true;
this.fiber = null;
}
@@ -341,15 +294,17 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
this.bdom!.remove();
}
// ---------------------------------------------------------------------------
// Some debug helpers
// ---------------------------------------------------------------------------
get name(): string {
return this.component.constructor.name;
}
get subscriptions(): ReturnType<typeof getSubscriptions> {
const render = batchedRenderFunctions.get(this);
return render ? getSubscriptions(render) : [];
cleanOutdatedChildren() {
const children = this.children;
for (const key in children) {
const node = children[key];
const status = node.status;
if (status !== STATUS.MOUNTED) {
delete children[key];
if (status !== STATUS.DESTROYED) {
node.destroy();
}
}
}
}
}
@@ -5,7 +5,7 @@ import type { Fiber } from "./fibers";
export const fibersInError: WeakMap<Fiber, any> = new WeakMap();
export const nodeErrorHandlers: WeakMap<ComponentNode, ((error: any) => void)[]> = new WeakMap();
function _handleError(node: ComponentNode | null, error: any): boolean {
function _handleError(node: ComponentNode | null, error: any, isFirstRound = false): boolean {
if (!node) {
return false;
}
@@ -16,19 +16,22 @@ function _handleError(node: ComponentNode | null, error: any): boolean {
const errorHandlers = nodeErrorHandlers.get(node);
if (errorHandlers) {
let handled = false;
let stopped = false;
// execute in the opposite order
for (let i = errorHandlers.length - 1; i >= 0; i--) {
try {
errorHandlers[i](error);
handled = true;
stopped = true;
break;
} catch (e) {
error = e;
}
}
if (handled) {
if (stopped) {
if (isFirstRound && fiber && fiber.node.fiber) {
fiber.root!.counter--;
}
return true;
}
}
@@ -51,7 +54,7 @@ export function handleError(params: ErrorParams) {
fibersInError.set(fiber.root!, error);
const handled = _handleError(node, error);
const handled = _handleError(node, error, true);
if (!handled) {
console.warn(`[Owl] Unhandled error. Destroying the root component`);
try {
@@ -1,4 +1,4 @@
import { BDom, mount } from "./blockdom";
import { BDom, mount } from "../blockdom";
import type { ComponentNode } from "./component_node";
import { fibersInError, handleError } from "./error_handling";
import { STATUS } from "./status";
@@ -6,7 +6,8 @@ import { STATUS } from "./status";
export function makeChildFiber(node: ComponentNode, parent: Fiber): Fiber {
let current = node.fiber;
if (current) {
cancelFibers(current.children);
let root = parent.root;
cancelFibers(root, current.children);
current.root = null;
}
return new Fiber(node, parent);
@@ -16,14 +17,9 @@ export function makeRootFiber(node: ComponentNode): Fiber {
let current = node.fiber;
if (current) {
let root = current.root!;
// lock root fiber because canceling children fibers may destroy components,
// which means any arbitrary code can be run in onWillDestroy, which may
// trigger new renderings
root.locked = true;
root.setCounter(root.counter + 1 - cancelFibers(current.children));
root.locked = false;
root.counter -= cancelFibers(root, current.children);
current.children = [];
current.childrenMap = {};
root.counter++;
current.bdom = null;
if (fibersInError.has(current)) {
fibersInError.delete(current);
@@ -39,37 +35,22 @@ export function makeRootFiber(node: ComponentNode): Fiber {
if (node.patched.length) {
fiber.patched.push(fiber);
}
return fiber;
}
function throwOnRender() {
throw new Error("Attempted to render cancelled fiber");
return fiber;
}
/**
* @returns number of not-yet rendered fibers cancelled
*/
function cancelFibers(fibers: Fiber[]): number {
function cancelFibers(root: any, fibers: Fiber[]): number {
let result = 0;
for (let fiber of fibers) {
let node = fiber.node;
fiber.render = throwOnRender;
if (node.status === STATUS.NEW) {
node.destroy();
}
node.fiber = null;
if (fiber.bdom) {
// if fiber has been rendered, this means that the component props have
// been updated. however, this fiber will not be patched to the dom, so
// it could happen that the next render compare the current props with
// the same props, and skip the render completely. With the next line,
// we kindly request the component code to force a render, so it works as
// expected.
node.forceNextRender = true;
} else {
fiber.node.fiber = null;
fiber.root = root;
if (!fiber.bdom) {
result++;
}
result += cancelFibers(fiber.children);
result += cancelFibers(root, fiber.children);
}
return result;
}
@@ -81,59 +62,19 @@ export class Fiber {
parent: Fiber | null;
children: Fiber[] = [];
appliedToDom = false;
deep: boolean = false;
childrenMap: ComponentNode["children"] = {};
constructor(node: ComponentNode, parent: Fiber | null) {
this.node = node;
this.parent = parent;
if (parent) {
this.deep = parent.deep;
const root = parent.root!;
root.setCounter(root.counter + 1);
root.counter++;
this.root = root;
parent.children.push(this);
} else {
this.root = this as any;
}
}
render() {
// if some parent has a fiber => register in followup
let prev = this.root!.node;
let scheduler = prev.app.scheduler;
let current = prev.parent;
while (current) {
if (current.fiber) {
let root = current.fiber.root!;
if (root.counter === 0 && prev.parentKey! in current.fiber.childrenMap) {
current = root.node;
} else {
scheduler.delayedRenders.push(this);
return;
}
}
prev = current;
current = current.parent;
}
// there are no current rendering from above => we can render
this._render();
}
_render() {
const node = this.node;
const root = this.root;
if (root) {
try {
(this.bdom as any) = true;
this.bdom = node.renderFn();
} catch (e) {
handleError({ node, error: e });
}
root.setCounter(root.counter - 1);
}
}
}
export class RootFiber extends Fiber {
@@ -168,7 +109,7 @@ export class RootFiber extends Fiber {
current = undefined;
// Step 2: patching the dom
node._patch();
node.patch();
this.locked = false;
// Step 4: calling all mounted lifecycle hooks
@@ -197,13 +138,6 @@ export class RootFiber extends Fiber {
handleError({ fiber: current || this, error: e });
}
}
setCounter(newValue: number) {
this.counter = newValue;
if (newValue === 0) {
this.node.app.scheduler.flush();
}
}
}
type Position = "first-child" | "last-child";
@@ -225,7 +159,6 @@ export class MountFiber extends RootFiber {
let current: Fiber | undefined = this;
try {
const node = this.node;
node.children = this.childrenMap;
(node.app.constructor as any).validateTarget(this.target);
if (node.bdom) {
// this is a complicated situation: if we mount a fiber with an existing
@@ -1,4 +1,4 @@
import { filterOutModifiersFromData } from "./blockdom/config";
import { filterOutModifiersFromData } from "../blockdom/config";
import { STATUS } from "./status";
export const mainEventHandler = (data: any, ev: Event, currentTarget?: EventTarget | null) => {
+71
View File
@@ -0,0 +1,71 @@
import { getCurrent } from "./component_node";
import { nodeErrorHandlers } from "./error_handling";
// -----------------------------------------------------------------------------
// hooks
// -----------------------------------------------------------------------------
export function onWillStart(fn: () => Promise<void> | void | any) {
const node = getCurrent();
node.willStart.push(fn.bind(node.component));
}
export function onWillUpdateProps(fn: (nextProps: any) => Promise<void> | void | any) {
const node = getCurrent();
node.willUpdateProps.push(fn.bind(node.component));
}
export function onMounted(fn: () => void | any) {
const node = getCurrent();
node.mounted.push(fn.bind(node.component));
}
export function onWillPatch(fn: () => Promise<void> | any | void) {
const node = getCurrent();
node.willPatch.unshift(fn.bind(node.component));
}
export function onPatched(fn: () => void | any) {
const node = getCurrent();
node.patched.push(fn.bind(node.component));
}
export function onWillUnmount(fn: () => Promise<void> | void | any) {
const node = getCurrent();
node.willUnmount.unshift(fn.bind(node.component));
}
export function onWillDestroy(fn: () => Promise<void> | void | any) {
const node = getCurrent();
node.willDestroy.push(fn.bind(node.component));
}
export function onWillRender(fn: () => void | any) {
const node = getCurrent();
const renderFn = node.renderFn;
node.renderFn = () => {
fn.call(node.component);
return renderFn();
};
}
export function onRendered(fn: () => void | any) {
const node = getCurrent();
const renderFn = node.renderFn;
node.renderFn = () => {
const result = renderFn();
fn.call(node.component);
return result;
};
}
type OnErrorCallback = (error: any) => void | any;
export function onError(callback: OnErrorCallback) {
const node = getCurrent();
let handlers = nodeErrorHandlers.get(node);
if (!handlers) {
handlers = [];
nodeErrorHandlers.set(node, handlers);
}
handlers.push(callback.bind(node.component));
}
+150
View File
@@ -0,0 +1,150 @@
import { ComponentConstructor } from "./component";
/**
* Apply default props (only top level).
*
* Note that this method does modify in place the props
*/
export function applyDefaultProps<P>(props: P, ComponentClass: ComponentConstructor<P>) {
const defaultProps = ComponentClass.defaultProps;
if (defaultProps) {
for (let propName in defaultProps) {
if ((props as any)[propName] === undefined) {
(props as any)[propName] = defaultProps[propName];
}
}
}
}
//------------------------------------------------------------------------------
// Prop validation helper
//------------------------------------------------------------------------------
function getPropDescription(staticProps: any) {
if (staticProps instanceof Array) {
return Object.fromEntries(
staticProps.map((p) => (p.endsWith("?") ? [p.slice(0, -1), false] : [p, true]))
);
}
return staticProps || { "*": true };
}
/**
* Validate the component props (or next props) against the (static) props
* description. This is potentially an expensive operation: it may needs to
* visit recursively the props and all the children to check if they are valid.
* This is why it is only done in 'dev' mode.
*/
export function validateProps<P>(name: string | ComponentConstructor<P>, props: P, parent?: any) {
const ComponentClass =
typeof name !== "string"
? name
: (parent.constructor.components[name] as ComponentConstructor<P> | undefined);
if (!ComponentClass) {
// this is an error, wrong component. We silently return here instead so the
// error is triggered by the usual path ('component' function)
return;
}
applyDefaultProps(props, ComponentClass);
const defaultProps = ComponentClass.defaultProps || {};
let propsDef = getPropDescription(ComponentClass.props);
const allowAdditionalProps = "*" in propsDef;
for (let propName in propsDef) {
if (propName === "*") {
continue;
}
const propDef = propsDef[propName];
let isMandatory = !!propDef;
if (typeof propDef === "object" && "optional" in propDef) {
isMandatory = !propDef.optional;
}
if (isMandatory && propName in defaultProps) {
throw new Error(
`A default value cannot be defined for a mandatory prop (name: '${propName}', component: ${ComponentClass.name})`
);
}
if ((props as any)[propName] === undefined) {
if (isMandatory) {
throw new Error(`Missing props '${propName}' (component '${ComponentClass.name}')`);
} else {
continue;
}
}
let isValid;
try {
isValid = isValidProp((props as any)[propName], propDef);
} catch (e) {
(e as Error).message = `Invalid prop '${propName}' in component ${ComponentClass.name} (${
(e as Error).message
})`;
throw e;
}
if (!isValid) {
throw new Error(`Invalid Prop '${propName}' in component '${ComponentClass.name}'`);
}
}
if (!allowAdditionalProps) {
for (let propName in props) {
if (!(propName in propsDef)) {
throw new Error(`Unknown prop '${propName}' given to component '${ComponentClass.name}'`);
}
}
}
}
/**
* Check if an invidual prop value matches its (static) prop definition
*/
function isValidProp(prop: any, propDef: any): boolean {
if (propDef === true) {
return true;
}
if (typeof propDef === "function") {
// Check if a value is constructed by some Constructor. Note that there is a
// slight abuse of language: we want to consider primitive values as well.
//
// So, even though 1 is not an instance of Number, we want to consider that
// it is valid.
if (typeof prop === "object") {
return prop instanceof propDef;
}
return typeof prop === propDef.name.toLowerCase();
} else if (propDef instanceof Array) {
// If this code is executed, this means that we want to check if a prop
// matches at least one of its descriptor.
let result = false;
for (let i = 0, iLen = propDef.length; i < iLen; i++) {
result = result || isValidProp(prop, propDef[i]);
}
return result;
}
// propsDef is an object
if (propDef.optional && prop === undefined) {
return true;
}
let result = propDef.type ? isValidProp(prop, propDef.type) : true;
if (propDef.validate) {
result = result && propDef.validate(prop);
}
if (propDef.type === Array && propDef.element) {
for (let i = 0, iLen = prop.length; i < iLen; i++) {
result = result && isValidProp(prop[i], propDef.element);
}
}
if (propDef.type === Object && propDef.shape) {
const shape = propDef.shape;
for (let key in shape) {
result = result && isValidProp(prop[key], shape[key]);
}
if (result) {
for (let propName in prop) {
if (!(propName in shape)) {
throw new Error(`unknown prop '${propName}'`);
}
}
}
}
return result;
}
+77
View File
@@ -0,0 +1,77 @@
import { fibersInError } from "./error_handling";
import { Fiber, RootFiber } from "./fibers";
import { STATUS } from "./status";
// -----------------------------------------------------------------------------
// Scheduler
// -----------------------------------------------------------------------------
export class Scheduler {
// capture the value of requestAnimationFrame as soon as possible, to avoid
// interactions with other code, such as test frameworks that override them
static requestAnimationFrame = window.requestAnimationFrame.bind(window);
tasks: Set<RootFiber> = new Set();
isRunning: boolean = false;
requestAnimationFrame: Window["requestAnimationFrame"];
constructor() {
this.requestAnimationFrame = Scheduler.requestAnimationFrame;
}
start() {
this.isRunning = true;
this.scheduleTasks();
}
stop() {
this.isRunning = false;
}
addFiber(fiber: Fiber) {
this.tasks.add(fiber.root!);
if (!this.isRunning) {
this.start();
}
}
/**
* Process all current tasks. This only applies to the fibers that are ready.
* Other tasks are left unchanged.
*/
flush() {
this.tasks.forEach((fiber) => {
if (fiber.root !== fiber) {
this.tasks.delete(fiber);
return;
}
const hasError = fibersInError.has(fiber);
if (hasError && fiber.counter !== 0) {
this.tasks.delete(fiber);
return;
}
if (fiber.node.status === STATUS.DESTROYED) {
this.tasks.delete(fiber);
return;
}
if (fiber.counter === 0) {
if (!hasError) {
fiber.complete();
}
this.tasks.delete(fiber);
}
});
if (this.tasks.size === 0) {
this.stop();
}
}
scheduleTasks() {
this.requestAnimationFrame(() => {
this.flush();
if (this.isRunning) {
this.scheduleTasks();
}
});
}
}
+9 -10
View File
@@ -1,6 +1,6 @@
import type { Env } from "./app";
import { getCurrent } from "./component_node";
import { onMounted, onPatched, onWillUnmount } from "./lifecycle_hooks";
import type { Env } from "./app/app";
import { getCurrent } from "./component/component_node";
import { onMounted, onPatched, onWillUnmount } from "./component/lifecycle_hooks";
// -----------------------------------------------------------------------------
// useRef
@@ -57,6 +57,7 @@ export function useChildSubEnv(envExtension: Env) {
// useEffect
// -----------------------------------------------------------------------------
const NO_OP = () => {};
/**
* @param {...any} dependencies the dependencies computed by computeDependencies
* @returns {void|(()=>void)} a cleanup function that reverses the side
@@ -77,11 +78,11 @@ type Effect = (...dependencies: any[]) => void | (() => void);
* NaN !== NaN, which will cause the effect to rerun on every patch.
*/
export function useEffect(effect: Effect, computeDependencies: () => any[] = () => [NaN]) {
let cleanup: (() => void) | void;
let cleanup: () => void;
let dependencies: any[];
onMounted(() => {
dependencies = computeDependencies();
cleanup = effect(...dependencies);
cleanup = effect(...dependencies) || NO_OP;
});
onPatched(() => {
@@ -89,14 +90,12 @@ export function useEffect(effect: Effect, computeDependencies: () => any[] = ()
const shouldReapply = newDeps.some((val, i) => val !== dependencies[i]);
if (shouldReapply) {
dependencies = newDeps;
if (cleanup) {
cleanup();
}
cleanup = effect(...dependencies);
cleanup();
cleanup = effect(...dependencies) || NO_OP;
}
});
onWillUnmount(() => cleanup && cleanup());
onWillUnmount(() => cleanup());
}
// -----------------------------------------------------------------------------
+58 -13
View File
@@ -1,16 +1,61 @@
import { TemplateSet } from "./runtime/template_set";
import { compile } from "./compiler";
import { UTILS } from "./app/template_helpers";
import {
config,
createBlock,
html,
list,
mount as blockMount,
multi,
patch,
remove,
text,
toggler,
comment,
} from "./blockdom";
import { mainEventHandler } from "./component/handler";
import { Portal } from "./portal";
export type { Reactive } from "./reactivity";
export * from "./runtime";
config.shouldNormalizeDom = false;
config.mainEventHandler = mainEventHandler;
(UTILS as any).Portal = Portal;
TemplateSet.prototype._compileTemplate = function _compileTemplate(
name: string,
template: string | Element
) {
return compile(template, {
name,
dev: this.dev,
translateFn: this.translateFn,
translatableAttributes: this.translatableAttributes,
});
export const blockDom = {
config,
// bdom entry points
mount: blockMount,
patch,
remove,
// bdom block types
list,
multi,
text,
toggler,
createBlock,
html,
comment,
};
export { App, mount } from "./app/app";
export { Component } from "./component/component";
export { useComponent } from "./component/component_node";
export { status } from "./component/status";
export { Memo } from "./memo";
export { xml } from "./app/template_set";
export { useState, reactive, markRaw, toRaw } from "./reactivity";
export { useEffect, useEnv, useExternalListener, useRef, useChildSubEnv, useSubEnv } from "./hooks";
export { EventBus, whenReady, loadFile, markup } from "./utils";
export {
onWillStart,
onMounted,
onWillUnmount,
onWillUpdateProps,
onWillPatch,
onPatched,
onWillRender,
onRendered,
onWillDestroy,
onError,
} from "./component/lifecycle_hooks";
export const __info__ = {};
+47
View File
@@ -0,0 +1,47 @@
import { Component } from "./component/component";
import type { ComponentNode } from "./component/component_node";
import { xml } from "./app/template_set";
import { Fiber } from "./component/fibers";
export class Memo extends Component {
static template = xml`<t t-slot="default"/>`;
constructor(props: any, env: any, node: ComponentNode) {
super(props, env, node);
// prevent patching process conditionally
let applyPatch = false;
const patchFn = node.patch;
node.patch = () => {
if (applyPatch) {
patchFn.call(node);
applyPatch = false;
}
};
// check props change, and render/apply patch if it changed
let prevProps = props;
const updateAndRender = node.updateAndRender;
node.updateAndRender = function (props: any, parentFiber: Fiber) {
const shouldUpdate = !shallowEqual(prevProps, props);
if (shouldUpdate) {
prevProps = props;
updateAndRender.call(node, props, parentFiber);
applyPatch = true;
}
return Promise.resolve();
};
}
}
/**
* we assume that each object have the same set of keys
*/
function shallowEqual(p1: any, p2: any): boolean {
for (let k in p1) {
if (k !== "slots" && p1[k] !== p2[k]) {
return false;
}
}
return true;
}
+4 -13
View File
@@ -1,6 +1,7 @@
import { onWillUnmount } from "./lifecycle_hooks";
import { onWillUnmount } from "./component/lifecycle_hooks";
import { xml } from "./app/template_set";
import { BDom, text, VNode } from "./blockdom";
import { Component } from "./component";
import { Component } from "./component/component";
const VText: any = text("").constructor;
@@ -52,18 +53,8 @@ class VPortal extends VText implements Partial<VNode<VPortal>> {
}
}
/**
* <t t-slot="default"/>
*/
export function portalTemplate(app: any, bdom: any, helpers: any) {
let { callSlot } = helpers;
return function template(ctx: any, node: any, key = "") {
return callSlot(ctx, node, key, "default", false, null);
};
}
export class Portal extends Component {
static template = "__portal__";
static template = xml`<t t-slot="default"/>`;
static props = {
target: {
type: String,
+253
View File
@@ -0,0 +1,253 @@
import { onWillDestroy } from "./component/lifecycle_hooks";
import { ComponentNode, getCurrent } from "./component/component_node";
import { batched, Callback } from "./utils";
// Allows to get the target of a Reactive (used for making a new Reactive from the underlying object)
const TARGET = Symbol("Target");
// Escape hatch to prevent reactivity system to turn something into a reactive
const SKIP = Symbol("Skip");
// Special key to subscribe to, to be notified of key creation/deletion
const KEYCHANGES = Symbol("Key changes");
type ObjectKey = string | number | symbol;
type Target = object;
export type Reactive<T extends Target = Target> = T & {
[TARGET]: any;
};
type NonReactive<T extends Target = Target> = T & {
[SKIP]: any;
};
const objectToString = Object.prototype.toString;
/**
* Checks whether a given value can be made into a reactive object.
*
* @param value the value to check
* @returns whether the value can be made reactive
*/
function canBeMadeReactive(value: any): boolean {
if (typeof value !== "object") {
return false;
}
// extract "RawType" from strings like "[object RawType]" => this lets us
// ignore many native objects such as Promise (whose toString is [object Promise])
// or Date ([object Date]).
const rawType = objectToString.call(value).slice(8, -1);
return rawType === "Object" || rawType === "Array";
}
/**
* Mark an object or array so that it is ignored by the reactivity system
*
* @param value the value to mark
* @returns the object itself
*/
export function markRaw<T extends Target>(value: T): NonReactive<T> {
(value as any)[SKIP] = true;
return value as NonReactive<T>;
}
/**
* Given a reactive objet, return the raw (non reactive) underlying object
*
* @param value a reactive value
* @returns the underlying value
*/
export function toRaw<T extends object>(value: Reactive<T>): T {
return value[TARGET];
}
const targetToKeysToCallbacks = new WeakMap<Target, Map<ObjectKey, Set<Callback>>>();
/**
* Observes a given key on a target with an callback. The callback will be
* called when the given key changes on the target.
*
* @param target the target whose key should be observed
* @param key the key to observe (or Symbol(KEYCHANGES) for key creation
* or deletion)
* @param callback the function to call when the key changes
*/
function observeTargetKey(target: Target, key: ObjectKey, callback: Callback): void {
if (!targetToKeysToCallbacks.get(target)) {
targetToKeysToCallbacks.set(target, new Map());
}
const keyToCallbacks = targetToKeysToCallbacks.get(target)!;
if (!keyToCallbacks.get(key)) {
keyToCallbacks.set(key, new Set());
}
keyToCallbacks.get(key)!.add(callback);
if (!callbacksToTargets.has(callback)) {
callbacksToTargets.set(callback, new Set());
}
callbacksToTargets.get(callback)!.add(target);
}
/**
* Notify Reactives that are observing a given target that a key has changed on
* the target.
*
* @param target target whose Reactives should be notified that the target was
* changed.
* @param key the key that changed (or Symbol `KEYCHANGES` if a key was created
* or deleted)
*/
function notifyReactives(target: Target, key: ObjectKey): void {
const keyToCallbacks = targetToKeysToCallbacks.get(target);
if (!keyToCallbacks) {
return;
}
const callbacks = keyToCallbacks.get(key);
if (!callbacks) {
return;
}
// Loop on copy because clearReactivesForCallback will modify the set in place
for (const callback of [...callbacks]) {
clearReactivesForCallback(callback);
callback();
}
}
const callbacksToTargets = new WeakMap<Callback, Set<Target>>();
/**
* Clears all subscriptions of the Reactives associated with a given callback.
*
* @param callback the callback for which the reactives need to be cleared
*/
function clearReactivesForCallback(callback: Callback): void {
const targetsToClear = callbacksToTargets.get(callback);
if (!targetsToClear) {
return;
}
for (const target of targetsToClear) {
const observedKeys = targetToKeysToCallbacks.get(target);
if (!observedKeys) {
continue;
}
for (const callbacks of observedKeys.values()) {
callbacks.delete(callback);
}
}
targetsToClear.clear();
}
const reactiveCache = new WeakMap<Target, WeakMap<Callback, Reactive>>();
/**
* Creates a reactive proxy for an object. Reading data on the reactive object
* subscribes to changes to the data. Writing data on the object will cause the
* notify callback to be called if there are suscriptions to that data. Nested
* objects and arrays are automatically made reactive as well.
*
* Whenever you are notified of a change, all subscriptions are cleared, and if
* you would like to be notified of any further changes, you should go read
* the underlying data again. We assume that if you don't go read it again after
* being notified, it means that you are no longer interested in that data.
*
* Subscriptions:
* + Reading a property on an object will subscribe you to changes in the value
* of that property.
* + Accessing an object keys (eg with Object.keys or with `for..in`) will
* subscribe you to the creation/deletion of keys. Checking the presence of a
* key on the object with 'in' has the same effect.
* - getOwnPropertyDescriptor does not currently subscribe you to the property.
* This is a choice that was made because changing a key's value will trigger
* this trap and we do not want to subscribe by writes. This also means that
* Object.hasOwnProperty doesn't subscribe as it goes through this trap.
*
* @param target the object for which to create a reactive proxy
* @param callback the function to call when an observed property of the
* reactive has changed
* @returns a proxy that tracks changes to it
*/
export function reactive<T extends Target>(
target: T,
callback: Callback = () => {}
): Reactive<T> | NonReactive<T> {
if (!canBeMadeReactive(target)) {
throw new Error(`Cannot make the given value reactive`);
}
if (SKIP in target) {
return target as NonReactive<T>;
}
const originalTarget = (target as Reactive)[TARGET];
if (originalTarget) {
return reactive(originalTarget, callback);
}
if (!reactiveCache.has(target)) {
reactiveCache.set(target, new Map());
}
const reactivesForTarget = reactiveCache.get(target)!;
if (!reactivesForTarget.has(callback)) {
const proxy = new Proxy(target, {
get(target: any, key: ObjectKey, proxy: Reactive<T>) {
if (key === TARGET) {
return target;
}
observeTargetKey(target, key, callback);
const value = Reflect.get(target, key, proxy);
if (!canBeMadeReactive(value)) {
return value;
}
return reactive(value, callback);
},
set(target, key, value, proxy) {
const isNewKey = !Object.hasOwnProperty.call(target, key);
const originalValue = Reflect.get(target, key, proxy);
const ret = Reflect.set(target, key, value, proxy);
if (isNewKey) {
notifyReactives(target, KEYCHANGES);
}
// While Array length may trigger the set trap, it's not actually set by this
// method but is updated behind the scenes, and the trap is not called with the
// new value. We disable the "same-value-optimization" for it because of that.
if (originalValue !== value || (Array.isArray(target) && key === "length")) {
notifyReactives(target, key);
}
return ret;
},
deleteProperty(target, key) {
const ret = Reflect.deleteProperty(target, key);
notifyReactives(target, KEYCHANGES);
notifyReactives(target, key);
return ret;
},
ownKeys(target) {
observeTargetKey(target, KEYCHANGES, callback);
return Reflect.ownKeys(target);
},
has(target, key) {
// TODO: this observes all key changes instead of only the presence of the argument key
observeTargetKey(target, KEYCHANGES, callback);
return Reflect.has(target, key);
},
});
reactivesForTarget.set(callback, proxy);
}
return reactivesForTarget.get(callback) as Reactive<T>;
}
const batchedRenderFunctions = new WeakMap<ComponentNode, Callback>();
/**
* Creates a reactive object that will be observed by the current component.
* Reading data from the returned object (eg during rendering) will cause the
* component to subscribe to that data and be rerendered when it changes.
*
* @param state the state to observe
* @returns a reactive object that will cause the component to re-render on
* relevant changes
* @see reactive
*/
export function useState<T extends object>(state: T): Reactive<T> | NonReactive<T> {
const node = getCurrent();
if (!batchedRenderFunctions.has(node)) {
batchedRenderFunctions.set(
node,
batched(() => node.render())
);
onWillDestroy(() => clearReactivesForCallback(render));
}
const render = batchedRenderFunctions.get(node)!;
const reactiveState = reactive(state, render);
return reactiveState;
}
-188
View File
@@ -1,188 +0,0 @@
import { Component, ComponentConstructor, Props } from "./component";
import { ComponentNode } from "./component_node";
import { nodeErrorHandlers } from "./error_handling";
import { Fiber, MountOptions } from "./fibers";
import { Scheduler } from "./scheduler";
import { STATUS } from "./status";
import { validateProps } from "./template_helpers";
import { TemplateSet, TemplateSetConfig } from "./template_set";
import { validateTarget } from "./utils";
// reimplement dev mode stuff see last change in 0f7a8289a6fb8387c3c1af41c6664b2a8448758f
export interface Env {
[key: string]: any;
}
export interface AppConfig<P, E> extends TemplateSetConfig {
props?: P;
env?: E;
test?: boolean;
warnIfNoStaticProps?: boolean;
}
let hasBeenLogged = false;
export const DEV_MSG = () => {
const hash = (window as any).owl ? (window as any).owl.__info__.hash : "master";
return `Owl is running in 'dev' mode.
This is not suitable for production use.
See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration for more information.`;
};
export class App<
T extends abstract new (...args: any) => any = any,
P extends object = any,
E = any
> extends TemplateSet {
static validateTarget = validateTarget;
Root: ComponentConstructor<P, E>;
props: P;
env: E;
scheduler = new Scheduler();
root: ComponentNode<P, E> | null = null;
warnIfNoStaticProps: boolean;
constructor(Root: ComponentConstructor<P, E>, config: AppConfig<P, E> = {}) {
super(config);
this.Root = Root;
if (config.test) {
this.dev = true;
}
this.warnIfNoStaticProps = config.warnIfNoStaticProps || false;
if (this.dev && !config.test && !hasBeenLogged) {
console.info(DEV_MSG());
hasBeenLogged = true;
}
const env = config.env || {};
const descrs = Object.getOwnPropertyDescriptors(env);
this.env = Object.freeze(Object.create(Object.getPrototypeOf(env), descrs));
this.props = config.props || ({} as P);
}
mount(target: HTMLElement, options?: MountOptions): Promise<Component<P, E> & InstanceType<T>> {
App.validateTarget(target);
if (this.dev) {
validateProps(this.Root, this.props, { __owl__: { app: this } });
}
const node = this.makeNode(this.Root, this.props);
const prom = this.mountNode(node, target, options);
this.root = node;
return prom;
}
makeNode(Component: ComponentConstructor, props: any): ComponentNode {
return new ComponentNode(Component, props, this, null, null);
}
mountNode(node: ComponentNode, target: HTMLElement, options?: MountOptions) {
const promise: any = new Promise((resolve, reject) => {
let isResolved = false;
// manually set a onMounted callback.
// that way, we are independant from the current node.
node.mounted.push(() => {
resolve(node.component);
isResolved = true;
});
// Manually add the last resort error handler on the node
let handlers = nodeErrorHandlers.get(node);
if (!handlers) {
handlers = [];
nodeErrorHandlers.set(node, handlers);
}
handlers.unshift((e) => {
if (isResolved) {
console.error(e);
} else {
reject(e);
}
throw e;
});
});
node.mountComponent(target, options);
return promise;
}
destroy() {
if (this.root) {
this.scheduler.flush();
this.root.destroy();
}
}
createComponent<P extends Props>(
name: string | null,
isStatic: boolean,
hasSlotsProp: boolean,
hasDynamicPropList: boolean,
hasNoProp: boolean
) {
const isDynamic = !isStatic;
function _arePropsDifferent(props1: Props, props2: Props): boolean {
for (let k in props1) {
if (props1[k] !== props2[k]) {
return true;
}
}
return hasDynamicPropList && Object.keys(props1).length !== Object.keys(props2).length;
}
const arePropsDifferent = hasSlotsProp
? (_1: any, _2: any) => true
: hasNoProp
? (_1: any, _2: any) => false
: _arePropsDifferent;
const updateAndRender = ComponentNode.prototype.updateAndRender;
const initiateRender = ComponentNode.prototype.initiateRender;
return (props: P, key: string, ctx: ComponentNode, parent: any, C: any) => {
let children = ctx.children;
let node: any = children[key];
if (
node &&
(node.status === STATUS.DESTROYED || (isDynamic && node.component.constructor !== C))
) {
node = undefined;
}
const parentFiber = ctx.fiber!;
if (node) {
if (arePropsDifferent(node.props, props) || parentFiber.deep || node.forceNextRender) {
node.forceNextRender = false;
updateAndRender.call(node, props, parentFiber);
}
} else {
// new component
if (isStatic) {
C = parent.constructor.components[name as any];
if (!C) {
throw new Error(`Cannot find the definition of component "${name}"`);
} else if (!(C.prototype instanceof Component)) {
throw new Error(
`"${name}" is not a Component. It must inherit from the Component class`
);
}
}
node = new ComponentNode(C, props, this, ctx, key);
children[key] = node;
initiateRender.call(node, new Fiber(node, parentFiber));
}
parentFiber.childrenMap[key] = node;
return node;
};
}
}
export async function mount<
T extends abstract new (...args: any) => any = any,
P extends object = any,
E = any
>(
C: T & ComponentConstructor<P, E>,
target: HTMLElement,
config: AppConfig<P, E> & MountOptions = {}
): Promise<Component<P, E> & InstanceType<T>> {
return new App(C, config).mount(target, config);
}
-101
View File
@@ -1,101 +0,0 @@
import { createEventHandler } from "./events";
import type { VNode } from "./index";
type EventsSpec = { [name: string]: number };
type Catcher = (child: VNode, handlers: any[]) => VNode;
export function createCatcher(eventsSpec: EventsSpec): Catcher {
const n = Object.keys(eventsSpec).length;
class VCatcher {
child: VNode;
handlerData: any[];
handlerFns: any[] = [];
parentEl?: HTMLElement | undefined;
afterNode: Text | null = null;
constructor(child: VNode, handlers: any[]) {
this.child = child;
this.handlerData = handlers;
}
mount(parent: HTMLElement, afterNode: Node | null) {
this.parentEl = parent;
this.child.mount(parent, afterNode);
this.afterNode = document.createTextNode("");
parent.insertBefore(this.afterNode, afterNode);
this.wrapHandlerData();
for (let name in eventsSpec) {
const index = eventsSpec[name];
const handler = createEventHandler(name);
this.handlerFns[index] = handler;
handler.setup.call(parent, this.handlerData[index]);
}
}
wrapHandlerData() {
for (let i = 0; i < n; i++) {
let handler = this.handlerData[i];
// handler = [...mods, fn, comp], so we need to replace second to last elem
let idx = handler.length - 2;
let origFn = handler[idx];
const self = this;
handler[idx] = function (ev: any) {
const target = ev.target;
let currentNode: any = self.child.firstNode();
const afterNode = self.afterNode;
while (currentNode !== afterNode) {
if (currentNode.contains(target)) {
return origFn.call(this, ev);
}
currentNode = currentNode.nextSibling;
}
};
}
}
moveBefore(other: VCatcher | null, afterNode: Node | null) {
this.child.moveBefore(other ? other.child : null, afterNode);
this.parentEl!.insertBefore(this.afterNode!, afterNode);
}
patch(other: VCatcher, withBeforeRemove: boolean) {
if (this === other) {
return;
}
this.handlerData = other.handlerData;
this.wrapHandlerData();
for (let i = 0; i < n; i++) {
this.handlerFns[i].update.call(this.parentEl!, this.handlerData[i]);
}
this.child.patch(other.child, withBeforeRemove);
}
beforeRemove() {
this.child.beforeRemove();
}
remove() {
for (let i = 0; i < n; i++) {
this.handlerFns[i].remove.call(this.parentEl!);
}
this.child.remove();
this.afterNode!.remove();
}
firstNode(): Node | undefined {
return this.child.firstNode();
}
toString(): string {
return this.child.toString();
}
}
return function (child: VNode, handlers: any[]): VNode<VCatcher> {
return new VCatcher(child, handlers);
};
}
-58
View File
@@ -1,58 +0,0 @@
import {
config,
createBlock,
html,
list,
mount as blockMount,
multi,
patch,
remove,
text,
toggler,
comment,
} from "./blockdom";
import { mainEventHandler } from "./event_handling";
export type { Reactive } from "./reactivity";
config.shouldNormalizeDom = false;
config.mainEventHandler = mainEventHandler;
export const blockDom = {
config,
// bdom entry points
mount: blockMount,
patch,
remove,
// bdom block types
list,
multi,
text,
toggler,
createBlock,
html,
comment,
};
export { App, mount } from "./app";
export { xml } from "./template_set";
export { Component } from "./component";
export { useComponent, useState } from "./component_node";
export { status } from "./status";
export { reactive, markRaw, toRaw } from "./reactivity";
export { useEffect, useEnv, useExternalListener, useRef, useChildSubEnv, useSubEnv } from "./hooks";
export { EventBus, whenReady, loadFile, markup } from "./utils";
export {
onWillStart,
onMounted,
onWillUnmount,
onWillUpdateProps,
onWillPatch,
onPatched,
onWillRender,
onRendered,
onWillDestroy,
onError,
} from "./lifecycle_hooks";
export { validate } from "./validation";
export const __info__ = {};
-122
View File
@@ -1,122 +0,0 @@
import { getCurrent } from "./component_node";
import { nodeErrorHandlers } from "./error_handling";
const TIMEOUT = Symbol("timeout");
function wrapError(fn: (...args: any[]) => any, hookName: string) {
const error = new Error(`The following error occurred in ${hookName}: `) as Error & {
cause: any;
};
const timeoutError = new Error(`${hookName}'s promise hasn't resolved after 3 seconds`);
const node = getCurrent();
return (...args: any[]) => {
try {
const result = fn(...args);
if (result instanceof Promise) {
if (hookName === "onWillStart" || hookName === "onWillUpdateProps") {
const fiber = node.fiber;
Promise.race([
result,
new Promise((resolve) => setTimeout(() => resolve(TIMEOUT), 3000)),
]).then((res) => {
if (res === TIMEOUT && node.fiber === fiber) {
console.warn(timeoutError);
}
});
}
return result.catch((cause) => {
error.cause = cause;
if (cause instanceof Error) {
error.message += `"${cause.message}"`;
}
throw error;
});
}
return result;
} catch (cause) {
if (cause instanceof Error) {
error.message += `"${cause.message}"`;
}
throw error;
}
};
}
// -----------------------------------------------------------------------------
// hooks
// -----------------------------------------------------------------------------
export function onWillStart(fn: () => Promise<void> | void | any) {
const node = getCurrent();
const decorate = node.app.dev ? wrapError : (fn: any) => fn;
node.willStart.push(decorate(fn.bind(node.component), "onWillStart"));
}
export function onWillUpdateProps(fn: (nextProps: any) => Promise<void> | void | any) {
const node = getCurrent();
const decorate = node.app.dev ? wrapError : (fn: any) => fn;
node.willUpdateProps.push(decorate(fn.bind(node.component), "onWillUpdateProps"));
}
export function onMounted(fn: () => void | any) {
const node = getCurrent();
const decorate = node.app.dev ? wrapError : (fn: any) => fn;
node.mounted.push(decorate(fn.bind(node.component), "onMounted"));
}
export function onWillPatch(fn: () => Promise<void> | any | void) {
const node = getCurrent();
const decorate = node.app.dev ? wrapError : (fn: any) => fn;
node.willPatch.unshift(decorate(fn.bind(node.component), "onWillPatch"));
}
export function onPatched(fn: () => void | any) {
const node = getCurrent();
const decorate = node.app.dev ? wrapError : (fn: any) => fn;
node.patched.push(decorate(fn.bind(node.component), "onPatched"));
}
export function onWillUnmount(fn: () => Promise<void> | void | any) {
const node = getCurrent();
const decorate = node.app.dev ? wrapError : (fn: any) => fn;
node.willUnmount.unshift(decorate(fn.bind(node.component), "onWillUnmount"));
}
export function onWillDestroy(fn: () => Promise<void> | void | any) {
const node = getCurrent();
const decorate = node.app.dev ? wrapError : (fn: any) => fn;
node.willDestroy.push(decorate(fn.bind(node.component), "onWillDestroy"));
}
export function onWillRender(fn: () => void | any) {
const node = getCurrent();
const renderFn = node.renderFn;
const decorate = node.app.dev ? wrapError : (fn: any) => fn;
fn = decorate(fn.bind(node.component), "onWillRender");
node.renderFn = () => {
fn();
return renderFn();
};
}
export function onRendered(fn: () => void | any) {
const node = getCurrent();
const renderFn = node.renderFn;
const decorate = node.app.dev ? wrapError : (fn: any) => fn;
fn = decorate(fn.bind(node.component), "onRendered");
node.renderFn = () => {
const result = renderFn();
fn();
return result;
};
}
type OnErrorCallback = (error: any) => void | any;
export function onError(callback: OnErrorCallback) {
const node = getCurrent();
let handlers = nodeErrorHandlers.get(node);
if (!handlers) {
handlers = [];
nodeErrorHandlers.set(node, handlers);
}
handlers.push(callback.bind(node.component));
}
-457
View File
@@ -1,457 +0,0 @@
import { Callback } from "./utils";
// Allows to get the target of a Reactive (used for making a new Reactive from the underlying object)
export const TARGET = Symbol("Target");
// Escape hatch to prevent reactivity system to turn something into a reactive
const SKIP = Symbol("Skip");
// Special key to subscribe to, to be notified of key creation/deletion
const KEYCHANGES = Symbol("Key changes");
type Target = object;
type Collection = Set<any> | Map<any, any> | WeakMap<any, any>;
type CollectionRawType = "Set" | "Map" | "WeakMap";
export type Reactive<T extends Target = Target> = T & {
[TARGET]: any;
};
export type NonReactive<T extends Target = Target> = T & {
[SKIP]: any;
};
const objectToString = Object.prototype.toString;
const objectHasOwnProperty = Object.prototype.hasOwnProperty;
const SUPPORTED_RAW_TYPES = new Set(["Object", "Array", "Set", "Map", "WeakMap"]);
const COLLECTION_RAWTYPES = new Set(["Set", "Map", "WeakMap"]);
/**
* extract "RawType" from strings like "[object RawType]" => this lets us ignore
* many native objects such as Promise (whose toString is [object Promise])
* or Date ([object Date]), while also supporting collections without using
* instanceof in a loop
*
* @param obj the object to check
* @returns the raw type of the object
*/
function rawType(obj: any) {
return objectToString.call(obj).slice(8, -1);
}
/**
* Checks whether a given value can be made into a reactive object.
*
* @param value the value to check
* @returns whether the value can be made reactive
*/
function canBeMadeReactive(value: any): boolean {
if (typeof value !== "object") {
return false;
}
return SUPPORTED_RAW_TYPES.has(rawType(value));
}
/**
* Creates a reactive from the given object/callback if possible and returns it,
* returns the original object otherwise.
*
* @param value the value make reactive
* @returns a reactive for the given object when possible, the original otherwise
*/
function possiblyReactive(val: any, cb: Callback) {
return canBeMadeReactive(val) ? reactive(val, cb) : val;
}
/**
* Mark an object or array so that it is ignored by the reactivity system
*
* @param value the value to mark
* @returns the object itself
*/
export function markRaw<T extends Target>(value: T): NonReactive<T> {
(value as any)[SKIP] = true;
return value as NonReactive<T>;
}
/**
* Given a reactive objet, return the raw (non reactive) underlying object
*
* @param value a reactive value
* @returns the underlying value
*/
export function toRaw<T extends object>(value: Reactive<T>): T {
return value[TARGET] || value;
}
const targetToKeysToCallbacks = new WeakMap<Target, Map<PropertyKey, Set<Callback>>>();
/**
* Observes a given key on a target with an callback. The callback will be
* called when the given key changes on the target.
*
* @param target the target whose key should be observed
* @param key the key to observe (or Symbol(KEYCHANGES) for key creation
* or deletion)
* @param callback the function to call when the key changes
*/
function observeTargetKey(target: Target, key: PropertyKey, callback: Callback): void {
if (!targetToKeysToCallbacks.get(target)) {
targetToKeysToCallbacks.set(target, new Map());
}
const keyToCallbacks = targetToKeysToCallbacks.get(target)!;
if (!keyToCallbacks.get(key)) {
keyToCallbacks.set(key, new Set());
}
keyToCallbacks.get(key)!.add(callback);
if (!callbacksToTargets.has(callback)) {
callbacksToTargets.set(callback, new Set());
}
callbacksToTargets.get(callback)!.add(target);
}
/**
* Notify Reactives that are observing a given target that a key has changed on
* the target.
*
* @param target target whose Reactives should be notified that the target was
* changed.
* @param key the key that changed (or Symbol `KEYCHANGES` if a key was created
* or deleted)
*/
function notifyReactives(target: Target, key: PropertyKey): void {
const keyToCallbacks = targetToKeysToCallbacks.get(target);
if (!keyToCallbacks) {
return;
}
const callbacks = keyToCallbacks.get(key);
if (!callbacks) {
return;
}
// Loop on copy because clearReactivesForCallback will modify the set in place
for (const callback of [...callbacks]) {
clearReactivesForCallback(callback);
callback();
}
}
const callbacksToTargets = new WeakMap<Callback, Set<Target>>();
/**
* Clears all subscriptions of the Reactives associated with a given callback.
*
* @param callback the callback for which the reactives need to be cleared
*/
export function clearReactivesForCallback(callback: Callback): void {
const targetsToClear = callbacksToTargets.get(callback);
if (!targetsToClear) {
return;
}
for (const target of targetsToClear) {
const observedKeys = targetToKeysToCallbacks.get(target);
if (!observedKeys) {
continue;
}
for (const callbacks of observedKeys.values()) {
callbacks.delete(callback);
}
}
targetsToClear.clear();
}
export function getSubscriptions(callback: Callback) {
const targets = callbacksToTargets.get(callback) || [];
return [...targets].map((target) => {
const keysToCallbacks = targetToKeysToCallbacks.get(target);
return {
target,
keys: keysToCallbacks ? [...keysToCallbacks.keys()] : [],
};
});
}
const reactiveCache = new WeakMap<Target, WeakMap<Callback, Reactive>>();
/**
* Creates a reactive proxy for an object. Reading data on the reactive object
* subscribes to changes to the data. Writing data on the object will cause the
* notify callback to be called if there are suscriptions to that data. Nested
* objects and arrays are automatically made reactive as well.
*
* Whenever you are notified of a change, all subscriptions are cleared, and if
* you would like to be notified of any further changes, you should go read
* the underlying data again. We assume that if you don't go read it again after
* being notified, it means that you are no longer interested in that data.
*
* Subscriptions:
* + Reading a property on an object will subscribe you to changes in the value
* of that property.
* + Accessing an object keys (eg with Object.keys or with `for..in`) will
* subscribe you to the creation/deletion of keys. Checking the presence of a
* key on the object with 'in' has the same effect.
* - getOwnPropertyDescriptor does not currently subscribe you to the property.
* This is a choice that was made because changing a key's value will trigger
* this trap and we do not want to subscribe by writes. This also means that
* Object.hasOwnProperty doesn't subscribe as it goes through this trap.
*
* @param target the object for which to create a reactive proxy
* @param callback the function to call when an observed property of the
* reactive has changed
* @returns a proxy that tracks changes to it
*/
export function reactive<T extends Target>(
target: T,
callback: Callback = () => {}
): Reactive<T> | NonReactive<T> {
if (!canBeMadeReactive(target)) {
throw new Error(`Cannot make the given value reactive`);
}
if (SKIP in target) {
return target as NonReactive<T>;
}
const originalTarget = (target as Reactive)[TARGET];
if (originalTarget) {
return reactive(originalTarget, callback);
}
if (!reactiveCache.has(target)) {
reactiveCache.set(target, new WeakMap());
}
const reactivesForTarget = reactiveCache.get(target)!;
if (!reactivesForTarget.has(callback)) {
const targetRawType = rawType(target);
const handler = COLLECTION_RAWTYPES.has(targetRawType)
? collectionsProxyHandler(target as Collection, callback, targetRawType as CollectionRawType)
: basicProxyHandler<T>(callback);
const proxy = new Proxy(target, handler as ProxyHandler<T>) as Reactive<T>;
reactivesForTarget.set(callback, proxy);
}
return reactivesForTarget.get(callback) as Reactive<T>;
}
/**
* Creates a basic proxy handler for regular objects and arrays.
*
* @param callback @see reactive
* @returns a proxy handler object
*/
function basicProxyHandler<T extends Target>(callback: Callback): ProxyHandler<T> {
return {
get(target: any, key: PropertyKey, proxy: Reactive<T>) {
if (key === TARGET) {
return target;
}
// non-writable non-configurable properties cannot be made reactive
const desc = Object.getOwnPropertyDescriptor(target, key);
if (desc && !desc.writable && !desc.configurable) {
return Reflect.get(target, key, proxy);
}
observeTargetKey(target, key, callback);
return possiblyReactive(Reflect.get(target, key, proxy), callback);
},
set(target, key, value, proxy) {
const isNewKey = !objectHasOwnProperty.call(target, key);
const originalValue = Reflect.get(target, key, proxy);
const ret = Reflect.set(target, key, value, proxy);
if (isNewKey) {
notifyReactives(target, KEYCHANGES);
}
// While Array length may trigger the set trap, it's not actually set by this
// method but is updated behind the scenes, and the trap is not called with the
// new value. We disable the "same-value-optimization" for it because of that.
if (originalValue !== value || (Array.isArray(target) && key === "length")) {
notifyReactives(target, key);
}
return ret;
},
deleteProperty(target, key) {
const ret = Reflect.deleteProperty(target, key);
// TODO: only notify when something was actually deleted
notifyReactives(target, KEYCHANGES);
notifyReactives(target, key);
return ret;
},
ownKeys(target) {
observeTargetKey(target, KEYCHANGES, callback);
return Reflect.ownKeys(target);
},
has(target, key) {
// TODO: this observes all key changes instead of only the presence of the argument key
// observing the key itself would observe value changes instead of presence changes
// so we may need a finer grained system to distinguish observing value vs presence.
observeTargetKey(target, KEYCHANGES, callback);
return Reflect.has(target, key);
},
} as ProxyHandler<T>;
}
/**
* Creates a function that will observe the key that is passed to it when called
* and delegates to the underlying method.
*
* @param methodName name of the method to delegate to
* @param target @see reactive
* @param callback @see reactive
*/
function makeKeyObserver(methodName: "has" | "get", target: any, callback: Callback) {
return (key: any) => {
key = toRaw(key);
observeTargetKey(target, key, callback);
return possiblyReactive(target[methodName](key), callback);
};
}
/**
* Creates an iterable that will delegate to the underlying iteration method and
* observe keys as necessary.
*
* @param methodName name of the method to delegate to
* @param target @see reactive
* @param callback @see reactive
*/
function makeIteratorObserver(
methodName: "keys" | "values" | "entries" | typeof Symbol.iterator,
target: any,
callback: Callback
) {
return function* () {
observeTargetKey(target, KEYCHANGES, callback);
const keys = target.keys();
for (const item of target[methodName]()) {
const key = keys.next().value;
observeTargetKey(target, key, callback);
yield possiblyReactive(item, callback);
}
};
}
/**
* Creates a forEach function that will delegate to forEach on the underlying
* collection while observing key changes, and keys as they're iterated over,
* and making the passed keys/values reactive.
*
* @param target @see reactive
* @param callback @see reactive
*/
function makeForEachObserver(target: any, callback: Callback) {
return function forEach(forEachCb: (val: any, key: any, target: any) => void, thisArg: any) {
observeTargetKey(target, KEYCHANGES, callback);
target.forEach(function (val: any, key: any, targetObj: any) {
observeTargetKey(target, key, callback);
forEachCb.call(
thisArg,
possiblyReactive(val, callback),
possiblyReactive(key, callback),
possiblyReactive(targetObj, callback)
);
}, thisArg);
};
}
/**
* Creates a function that will delegate to an underlying method, and check if
* that method has modified the presence or value of a key, and notify the
* reactives appropriately.
*
* @param setterName name of the method to delegate to
* @param getterName name of the method which should be used to retrieve the
* value before calling the delegate method for comparison purposes
* @param target @see reactive
*/
function delegateAndNotify(
setterName: "set" | "add" | "delete",
getterName: "has" | "get",
target: any
) {
return (key: any, value: any) => {
key = toRaw(key);
const hadKey = target.has(key);
const originalValue = target[getterName](key);
const ret = target[setterName](key, value);
const hasKey = target.has(key);
if (hadKey !== hasKey) {
notifyReactives(target, KEYCHANGES);
}
if (originalValue !== value) {
notifyReactives(target, key);
}
return ret;
};
}
/**
* Creates a function that will clear the underlying collection and notify that
* the keys of the collection have changed.
*
* @param target @see reactive
*/
function makeClearNotifier(target: Map<any, any> | Set<any>) {
return () => {
const allKeys = [...target.keys()];
target.clear();
notifyReactives(target, KEYCHANGES);
for (const key of allKeys) {
notifyReactives(target, key);
}
};
}
/**
* Maps raw type of an object to an object containing functions that can be used
* to build an appropritate proxy handler for that raw type. Eg: when making a
* reactive set, calling the has method should mark the key that is being
* retrieved as observed, and calling the add or delete method should notify the
* reactives that the key which is being added or deleted has been modified.
*/
const rawTypeToFuncHandlers = {
Set: (target: any, callback: Callback) => ({
has: makeKeyObserver("has", target, callback),
add: delegateAndNotify("add", "has", target),
delete: delegateAndNotify("delete", "has", target),
keys: makeIteratorObserver("keys", target, callback),
values: makeIteratorObserver("values", target, callback),
entries: makeIteratorObserver("entries", target, callback),
[Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target, callback),
forEach: makeForEachObserver(target, callback),
clear: makeClearNotifier(target),
get size() {
observeTargetKey(target, KEYCHANGES, callback);
return target.size;
},
}),
Map: (target: any, callback: Callback) => ({
has: makeKeyObserver("has", target, callback),
get: makeKeyObserver("get", target, callback),
set: delegateAndNotify("set", "get", target),
delete: delegateAndNotify("delete", "has", target),
keys: makeIteratorObserver("keys", target, callback),
values: makeIteratorObserver("values", target, callback),
entries: makeIteratorObserver("entries", target, callback),
[Symbol.iterator]: makeIteratorObserver(Symbol.iterator, target, callback),
forEach: makeForEachObserver(target, callback),
clear: makeClearNotifier(target),
get size() {
observeTargetKey(target, KEYCHANGES, callback);
return target.size;
},
}),
WeakMap: (target: any, callback: Callback) => ({
has: makeKeyObserver("has", target, callback),
get: makeKeyObserver("get", target, callback),
set: delegateAndNotify("set", "get", target),
delete: delegateAndNotify("delete", "has", target),
}),
};
/**
* Creates a proxy handler for collections (Set/Map/WeakMap)
*
* @param callback @see reactive
* @param target @see reactive
* @returns a proxy handler object
*/
function collectionsProxyHandler<T extends Collection>(
target: T,
callback: Callback,
targetRawType: CollectionRawType
): ProxyHandler<T> {
// TODO: if performance is an issue we can create the special handlers lazily when each
// property is read.
const specialHandlers = rawTypeToFuncHandlers[targetRawType](target, callback);
return Object.assign(basicProxyHandler(callback), {
get(target: any, key: PropertyKey) {
if (key === TARGET) {
return target;
}
if (objectHasOwnProperty.call(specialHandlers, key)) {
return (specialHandlers as any)[key];
}
observeTargetKey(target, key, callback);
return possiblyReactive(target[key], callback);
},
}) as ProxyHandler<T>;
}
-76
View File
@@ -1,76 +0,0 @@
import { fibersInError } from "./error_handling";
import { Fiber, RootFiber } from "./fibers";
import { STATUS } from "./status";
// -----------------------------------------------------------------------------
// Scheduler
// -----------------------------------------------------------------------------
export class Scheduler {
// capture the value of requestAnimationFrame as soon as possible, to avoid
// interactions with other code, such as test frameworks that override them
static requestAnimationFrame = window.requestAnimationFrame.bind(window);
tasks: Set<RootFiber> = new Set();
requestAnimationFrame: Window["requestAnimationFrame"];
frame: number = 0;
delayedRenders: Fiber[] = [];
constructor() {
this.requestAnimationFrame = Scheduler.requestAnimationFrame;
}
addFiber(fiber: Fiber) {
this.tasks.add(fiber.root!);
}
/**
* Process all current tasks. This only applies to the fibers that are ready.
* Other tasks are left unchanged.
*/
flush() {
if (this.delayedRenders.length) {
let renders = this.delayedRenders;
this.delayedRenders = [];
for (let f of renders) {
if (f.root && f.node.status !== STATUS.DESTROYED && f.node.fiber === f) {
f.render();
}
}
}
if (this.frame === 0) {
this.frame = this.requestAnimationFrame(() => {
this.frame = 0;
this.tasks.forEach((fiber) => this.processFiber(fiber));
for (let task of this.tasks) {
if (task.node.status === STATUS.DESTROYED) {
this.tasks.delete(task);
}
}
});
}
}
processFiber(fiber: RootFiber) {
if (fiber.root !== fiber) {
this.tasks.delete(fiber);
return;
}
const hasError = fibersInError.has(fiber);
if (hasError && fiber.counter !== 0) {
this.tasks.delete(fiber);
return;
}
if (fiber.node.status === STATUS.DESTROYED) {
this.tasks.delete(fiber);
return;
}
if (fiber.counter === 0) {
if (!hasError) {
fiber.complete();
}
this.tasks.delete(fiber);
}
}
}
-167
View File
@@ -1,167 +0,0 @@
type BaseType =
| typeof String
| typeof Boolean
| typeof Number
| typeof Date
| typeof Object
| typeof Array
| true
| "*";
interface TypeInfo {
type?: TypeDescription;
optional?: boolean;
validate?: Function;
shape?: Schema;
element?: TypeDescription;
}
type ValueType = { value: any };
type TypeDescription = BaseType | TypeInfo | ValueType | TypeDescription[];
type SimplifiedSchema = string[];
type NormalizedSchema = { [key: string]: TypeDescription };
export type Schema = SimplifiedSchema | NormalizedSchema;
// -----------------------------------------------------------------------------
// helpers
// -----------------------------------------------------------------------------
const isUnionType = (t: TypeDescription): t is TypeDescription[] => Array.isArray(t);
const isBaseType = (t: TypeDescription): t is BaseType => typeof t !== "object";
const isValueType = (t: TypeDescription): t is ValueType =>
typeof t === "object" && t && "value" in t;
export function isOptional(t: TypeDescription): Boolean {
return typeof t === "object" && "optional" in t ? t.optional || false : false;
}
function describeType(type: BaseType): string {
return type === "*" || type === true ? "value" : type.name.toLowerCase();
}
function describe(info: TypeDescription): string {
if (isBaseType(info)) {
return describeType(info);
} else if (isUnionType(info)) {
return info.map(describe).join(" or ");
} else if (isValueType(info)) {
return String(info.value);
}
if ("element" in info) {
return `list of ${describe({ type: info.element, optional: false })}s`;
}
if ("shape" in (info as TypeInfo)) {
return `object`;
}
return describe(info.type || "*");
}
function toSchema(spec: SimplifiedSchema): NormalizedSchema {
return Object.fromEntries(
spec.map((e) =>
e.endsWith("?") ? [e.slice(0, -1), { optional: true }] : [e, { type: "*", optional: false }]
)
);
}
/**
* Main validate function
*/
export function validate(obj: { [key: string]: any }, spec: Schema) {
let errors = validateSchema(obj, spec);
if (errors.length) {
throw new Error("Invalid object: " + errors.join(", "));
}
}
/**
* Helper validate function, to get the list of errors. useful if one want to
* manipulate the errors without parsing an error object
*/
export function validateSchema(obj: { [key: string]: any }, schema: Schema): string[] {
if (Array.isArray(schema)) {
schema = toSchema(schema);
}
let errors = [];
// check if each value in obj has correct shape
for (let key in obj) {
if (key in schema) {
let result = validateType(key, obj[key], schema[key]);
if (result) {
errors.push(result);
}
} else if (!("*" in schema)) {
errors.push(`unknown key '${key}'`);
}
}
// check that all specified keys are defined in obj
for (let key in schema) {
const spec = schema[key];
if (key !== "*" && !isOptional(spec) && !(key in obj)) {
const isObj = typeof spec === "object" && !Array.isArray(spec);
const isAny = spec === "*" || (isObj && "type" in spec ? spec.type === "*" : isObj);
let detail = isAny ? "" : ` (should be a ${describe(spec)})`;
errors.push(`'${key}' is missing${detail}`);
}
}
return errors;
}
function validateBaseType(key: string, value: any, type: BaseType): string | null {
if (typeof type === "function") {
if (typeof value === "object") {
if (!(value instanceof type)) {
return `'${key}' is not a ${describeType(type)}`;
}
} else if (typeof value !== type.name.toLowerCase()) {
return `'${key}' is not a ${describeType(type)}`;
}
}
return null;
}
function validateArrayType(key: string, value: any, descr: TypeDescription): string | null {
if (!Array.isArray(value)) {
return `'${key}' is not a list of ${describe(descr)}s`;
}
for (let i = 0; i < value.length; i++) {
const error = validateType(`${key}[${i}]`, value[i], descr);
if (error) {
return error;
}
}
return null;
}
function validateType(key: string, value: any, descr: TypeDescription): string | null {
if (value === undefined) {
return isOptional(descr) ? null : `'${key}' is undefined (should be a ${describe(descr)})`;
} else if (isBaseType(descr)) {
return validateBaseType(key, value, descr);
} else if (isValueType(descr)) {
return value === descr.value ? null : `'${key}' is not equal to '${descr.value}'`;
} else if (isUnionType(descr)) {
let validDescr = descr.find((p) => !validateType(key, value, p));
return validDescr ? null : `'${key}' is not a ${describe(descr)}`;
}
let result: string | null = null;
if ("element" in descr) {
result = validateArrayType(key, value, descr.element!);
} else if ("shape" in descr && !result) {
if (typeof value !== "object" || Array.isArray(value)) {
result = `'${key}' is not an object`;
} else {
const errors = validateSchema(value, descr.shape!);
if (errors.length) {
result = `'${key}' has not the correct shape (${errors.join(", ")})`;
}
}
}
if ("type" in descr && !result) {
result = validateType(key, value, descr.type!);
}
if ("validate" in descr && !result) {
result = !descr.validate!(value) ? `'${key}' is not valid` : null;
}
return result;
}
+4 -6
View File
@@ -15,13 +15,11 @@ export function batched(callback: Callback): Callback {
await Promise.resolve();
if (!called) {
called = true;
// wait for all calls in this microtick to fall through before resetting "called"
// so that only the first call to the batched function calls the original callback.
// Schedule this before calling the callback so that calls to the batched function
// within the callback will proceed only after resetting called to false, and have
// a chance to execute the callback again
Promise.resolve().then(() => (called = false));
callback();
// wait for all calls in this microtick to fall through before resetting "called"
// so that only the first call to the batched function calls the original callback
await Promise.resolve();
called = false;
}
};
}
+50 -79
View File
@@ -47,18 +47,16 @@ exports[`Reactivity: useState concurrent renderings 3`] = `
`;
exports[`Reactivity: useState destroyed component before being mounted is inactive 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Child\`, true, false, false, true);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
let b2;
if (ctx['state'].flag) {
b2 = comp1({}, key + \`__1\`, node, this, null);
b2 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
}
return block1([], [b2]);
}
@@ -66,10 +64,9 @@ exports[`Reactivity: useState destroyed component before being mounted is inacti
`;
exports[`Reactivity: useState destroyed component before being mounted is inactive 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<span><block-text-0/></span>\`);
@@ -81,18 +78,16 @@ exports[`Reactivity: useState destroyed component before being mounted is inacti
`;
exports[`Reactivity: useState destroyed component is inactive 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Child\`, true, false, false, true);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
let b2;
if (ctx['state'].flag) {
b2 = comp1({}, key + \`__1\`, node, this, null);
b2 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
}
return block1([], [b2]);
}
@@ -100,10 +95,9 @@ exports[`Reactivity: useState destroyed component is inactive 1`] = `
`;
exports[`Reactivity: useState destroyed component is inactive 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<span><block-text-0/></span>\`);
@@ -115,10 +109,9 @@ exports[`Reactivity: useState destroyed component is inactive 2`] = `
`;
exports[`Reactivity: useState one components can subscribe twice to same context 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-text-0/><block-text-1/></div>\`);
@@ -131,16 +124,14 @@ exports[`Reactivity: useState one components can subscribe twice to same context
`;
exports[`Reactivity: useState parent and children subscribed to same context 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Child\`, true, false, false, true);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/><block-text-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = comp1({}, key + \`__1\`, node, this, null);
let b2 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
let txt1 = ctx['contextObj'].b;
return block1([txt1], [b2]);
}
@@ -148,10 +139,9 @@ exports[`Reactivity: useState parent and children subscribed to same context 1`]
`;
exports[`Reactivity: useState parent and children subscribed to same context 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<span><block-text-0/></span>\`);
@@ -226,28 +216,24 @@ exports[`Reactivity: useState several nodes on different level use same context
`;
exports[`Reactivity: useState two components are updated in parallel 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Child\`, true, false, false, true);
const comp2 = app.createComponent(\`Child\`, true, false, false, true);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = comp1({}, key + \`__1\`, node, this, null);
const b3 = comp2({}, key + \`__2\`, node, this, null);
let b2 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
let b3 = component(\`Child\`, {}, key + \`__2\`, node, ctx);
return block1([], [b2, b3]);
}
}"
`;
exports[`Reactivity: useState two components are updated in parallel 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<span><block-text-0/></span>\`);
@@ -259,28 +245,24 @@ exports[`Reactivity: useState two components are updated in parallel 2`] = `
`;
exports[`Reactivity: useState two components can subscribe to same context 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Child\`, true, false, false, true);
const comp2 = app.createComponent(\`Child\`, true, false, false, true);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = comp1({}, key + \`__1\`, node, this, null);
const b3 = comp2({}, key + \`__2\`, node, this, null);
let b2 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
let b3 = component(\`Child\`, {}, key + \`__2\`, node, ctx);
return block1([], [b2, b3]);
}
}"
`;
exports[`Reactivity: useState two components can subscribe to same context 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<span><block-text-0/></span>\`);
@@ -292,28 +274,24 @@ exports[`Reactivity: useState two components can subscribe to same context 2`] =
`;
exports[`Reactivity: useState two independent components on different levels are updated in parallel 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Child\`, true, false, false, true);
const comp2 = app.createComponent(\`Parent\`, true, false, false, true);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = comp1({}, key + \`__1\`, node, this, null);
const b3 = comp2({}, key + \`__2\`, node, this, null);
let b2 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
let b3 = component(\`Parent\`, {}, key + \`__2\`, node, ctx);
return block1([], [b2, b3]);
}
}"
`;
exports[`Reactivity: useState two independent components on different levels are updated in parallel 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<span><block-text-0/></span>\`);
@@ -325,26 +303,23 @@ exports[`Reactivity: useState two independent components on different levels are
`;
exports[`Reactivity: useState two independent components on different levels are updated in parallel 3`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const comp1 = app.createComponent(\`Child\`, true, false, false, true);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = comp1({}, key + \`__1\`, node, this, null);
let b2 = component(\`Child\`, {}, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`Reactivity: useState useContext=useState hook is reactive, for one component 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -356,25 +331,23 @@ exports[`Reactivity: useState useContext=useState hook is reactive, for one comp
`;
exports[`Reactivity: useState useless atoms should be deleted 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
const comp1 = app.createComponent(\`Quantity\`, true, false, false, false);
let block1 = createBlock(\`<div><block-child-0/> Total: <block-text-0/> Count: <block-text-1/></div>\`);
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(Object.keys(ctx['state']));;
const [k_block2, v_block2, l_block2, c_block2] = prepareList(Object.keys(ctx['state']));
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`id\`] = v_block2[i1];
const key1 = ctx['id'];
c_block2[i1] = withKey(comp1({id: ctx['id']}, key + \`__1__\${key1}\`, node, this, null), key1);
let key1 = ctx['id'];
c_block2[i1] = withKey(component(\`Quantity\`, {id: ctx['id']}, key + \`__1__\${key1}\`, node, ctx), key1);
}
ctx = ctx.__proto__;
const b2 = list(c_block2);
let b2 = list(c_block2);
let txt1 = ctx['total'];
let txt2 = Object.keys(ctx['state']).length;
return block1([txt1, txt2], [b2]);
@@ -383,10 +356,9 @@ exports[`Reactivity: useState useless atoms should be deleted 1`] = `
`;
exports[`Reactivity: useState useless atoms should be deleted 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -398,10 +370,9 @@ exports[`Reactivity: useState useless atoms should be deleted 2`] = `
`;
exports[`Reactivity: useState very simple use, with initial value 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
+6 -24
View File
@@ -1,10 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`app App supports env with getters/setters 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-text-0/> <block-text-1/></div>\`);
@@ -17,10 +16,9 @@ exports[`app App supports env with getters/setters 1`] = `
`;
exports[`app can configure an app with props 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -32,10 +30,9 @@ exports[`app can configure an app with props 1`] = `
`;
exports[`app destroy remove the widget from the DOM 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div/>\`);
@@ -44,18 +41,3 @@ exports[`app destroy remove the widget from the DOM 1`] = `
}
}"
`;
exports[`app warnIfNoStaticProps works as expected 1`] = `
"function anonymous(app, bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
let txt1 = ctx['message'];
return block1([txt1]);
}
}"
`;
+2 -19
View File
@@ -1,5 +1,5 @@
import { App, Component, mount, xml } from "../../src";
import { status } from "../../src/runtime/status";
import { App, Component, xml } from "../../src";
import { status } from "../../src/component/status";
import { makeTestFixture, snapshotEverything, nextTick, elem } from "../helpers";
let fixture: HTMLElement;
@@ -59,21 +59,4 @@ describe("app", () => {
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div>333</div>");
});
test("warnIfNoStaticProps works as expected", async () => {
let originalconsoleWarn = console.warn;
let mockConsoleWarn = jest.fn(() => {});
console.warn = mockConsoleWarn;
class Root extends Component {
static template = xml`<div t-esc="message"/>`;
}
await mount(Root, fixture, { dev: true, props: { messge: "hey" }, warnIfNoStaticProps: true });
console.warn = originalconsoleWarn;
expect(mockConsoleWarn).toBeCalledWith(
"Component 'Root' does not have a static props description"
);
});
});
+1 -1
View File
@@ -1,4 +1,4 @@
import { createBlock, mount, patch, remove, text } from "../../src/runtime/blockdom";
import { createBlock, mount, patch, remove, text } from "../../src/blockdom";
import { makeTestFixture } from "./helpers";
//------------------------------------------------------------------------------
+1 -22
View File
@@ -1,4 +1,4 @@
import { mount, patch, createBlock } from "../../src/runtime/blockdom";
import { mount, patch, createBlock } from "../../src/blockdom";
import { makeTestFixture } from "./helpers";
//------------------------------------------------------------------------------
@@ -169,27 +169,6 @@ describe("properties", () => {
expect(input.value).toBe("potato");
});
test("input with value attribute, and falsy value given", () => {
const block = createBlock(`<input block-attribute-0="value"/>`);
const tree = block([undefined]);
mount(tree, fixture);
const input = fixture.querySelector("input")!;
expect(input.value).toBe("");
patch(tree, block([null]));
expect(input.value).toBe("");
patch(tree, block([0]));
expect(input.value).toBe("0");
patch(tree, block([""]));
expect(input.value).toBe("");
patch(tree, block([false]));
expect(input.value).toBe("");
});
test("input type=checkbox with checked attribute", () => {
// render input with initial value
const block = createBlock(`<input type="checkbox" block-attribute-0="checked"/>`);
+1 -1
View File
@@ -1,4 +1,4 @@
import { mount, createBlock, multi, config, patch } from "../../src/runtime/blockdom";
import { mount, createBlock, multi, config, patch } from "../../src/blockdom";
// import { defaultHandler, setupMainHandler } from "../../src/bdom/block";
import { makeTestFixture } from "./helpers";
+1 -1
View File
@@ -1,4 +1,4 @@
import { createBlock, mount, patch, remove } from "../../src/runtime/blockdom";
import { createBlock, mount, patch, remove } from "../../src/blockdom";
import { logStep } from "../helpers";
import { makeTestFixture } from "./helpers";
+1 -1
View File
@@ -1,4 +1,4 @@
import { comment, mount } from "../../src/runtime/blockdom";
import { comment, mount } from "../../src/blockdom";
import { makeTestFixture } from "./helpers";
//------------------------------------------------------------------------------
-54
View File
@@ -1,54 +0,0 @@
import { config, createBlock, createCatcher, mount } from "../../src/runtime/blockdom";
import { makeTestFixture } from "./helpers";
import { mainEventHandler } from "../../src/runtime/event_handling";
//------------------------------------------------------------------------------
// Setup and helpers
//------------------------------------------------------------------------------
let fixture: HTMLElement;
config.mainEventHandler = mainEventHandler;
beforeEach(() => {
fixture = makeTestFixture();
});
afterEach(() => {
fixture.remove();
});
test("simple event catcher", async () => {
const catcher = createCatcher({ click: 0 });
const block = createBlock("<div></div>");
let n = 0;
let ctx = {};
let handler = [() => n++, ctx];
const tree = catcher(block(), [handler]);
mount(tree, fixture);
expect(fixture.innerHTML).toBe("<div></div>");
expect(fixture.firstChild).toBeInstanceOf(HTMLDivElement);
expect(n).toBe(0);
(fixture.firstChild as HTMLDivElement).click();
expect(n).toBe(1);
});
test("do not catch events outside of itself", async () => {
const catcher = createCatcher({ click: 0 });
const childBlock = createBlock("<div></div>");
const parentBlock = createBlock("<button><block-child-0/></button>");
let n = 0;
let ctx = {};
let handler = [() => n++, ctx];
const tree = parentBlock([], [catcher(childBlock(), [handler])]);
mount(tree, fixture);
expect(fixture.innerHTML).toBe("<button><div></div></button>");
expect(n).toBe(0);
fixture.querySelector("div")!.click();
expect(n).toBe(1);
fixture.querySelector("button")!.click();
expect(n).toBe(1);
});
+1 -1
View File
@@ -1,4 +1,4 @@
import { html, mount, patch, text } from "../../src/runtime/blockdom";
import { html, mount, patch, text } from "../../src/blockdom";
import { makeTestFixture } from "./helpers";
//------------------------------------------------------------------------------
+1 -10
View File
@@ -1,13 +1,4 @@
import {
list,
mount,
multi,
patch,
text,
createBlock,
VNode,
withKey,
} from "../../src/runtime/blockdom";
import { list, mount, multi, patch, text, createBlock, VNode, withKey } from "../../src/blockdom";
import { makeTestFixture } from "./helpers";
//------------------------------------------------------------------------------
+1 -1
View File
@@ -1,4 +1,4 @@
import { mount, multi, patch, text, createBlock } from "../../src/runtime/blockdom";
import { mount, multi, patch, text, createBlock } from "../../src/blockdom";
import { makeTestFixture } from "./helpers";
//------------------------------------------------------------------------------
+1 -1
View File
@@ -1,4 +1,4 @@
import { createBlock, mount } from "../../src/runtime/blockdom";
import { createBlock, mount } from "../../src/blockdom";
import { makeTestFixture } from "./helpers";
//------------------------------------------------------------------------------
+1 -1
View File
@@ -1,4 +1,4 @@
import { createBlock, mount, multi, patch } from "../../src/runtime/blockdom";
import { createBlock, mount, multi, patch } from "../../src/blockdom";
import { makeTestFixture } from "./helpers";
//------------------------------------------------------------------------------
+1 -1
View File
@@ -1,4 +1,4 @@
import { mount, patch, remove, text } from "../../src/runtime/blockdom";
import { mount, patch, remove, text } from "../../src/blockdom";
import { makeTestFixture } from "./helpers";
//------------------------------------------------------------------------------
+1 -1
View File
@@ -1,4 +1,4 @@
import { createBlock, mount, patch, text, toggler } from "../../src/runtime/blockdom";
import { createBlock, mount, patch, text, toggler } from "../../src/blockdom";
import { makeTestFixture } from "./helpers";
//------------------------------------------------------------------------------
@@ -1,10 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`attributes changing a class with t-att-class (preexisting class 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div class=\\"hoy\\" block-attribute-0=\\"class\\"/>\`);
@@ -16,10 +15,9 @@ exports[`attributes changing a class with t-att-class (preexisting class 1`] = `
`;
exports[`attributes changing a class with t-att-class 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"class\\"/>\`);
@@ -31,10 +29,9 @@ exports[`attributes changing a class with t-att-class 1`] = `
`;
exports[`attributes changing an attribute with t-att- 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"value\\"/>\`);
@@ -46,10 +43,9 @@ exports[`attributes changing an attribute with t-att- 1`] = `
`;
exports[`attributes class and t-att-class should combine together 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"class\\" class=\\"hello\\"/>\`);
@@ -61,10 +57,9 @@ exports[`attributes class and t-att-class should combine together 1`] = `
`;
exports[`attributes class and t-attf-class with ternary operation 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div class=\\"hello\\" block-attribute-0=\\"class\\"/>\`);
@@ -76,10 +71,9 @@ exports[`attributes class and t-attf-class with ternary operation 1`] = `
`;
exports[`attributes dynamic attribute evaluating to 0 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"foo\\"/>\`);
@@ -91,10 +85,9 @@ exports[`attributes dynamic attribute evaluating to 0 1`] = `
`;
exports[`attributes dynamic attribute falsy variable 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"foo\\"/>\`);
@@ -106,10 +99,9 @@ exports[`attributes dynamic attribute falsy variable 1`] = `
`;
exports[`attributes dynamic attribute with a dash 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"data-action-id\\"/>\`);
@@ -121,10 +113,9 @@ exports[`attributes dynamic attribute with a dash 1`] = `
`;
exports[`attributes dynamic attributes 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"foo\\"/>\`);
@@ -135,26 +126,10 @@ exports[`attributes dynamic attributes 1`] = `
}"
`;
exports[`attributes dynamic attributes with backticks 1`] = `
"function anonymous(app, bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"foo\\"/>\`);
return function template(ctx, node, key = \\"\\") {
let attr1 = \`bar\`;
return block1([attr1]);
}
}"
`;
exports[`attributes dynamic class attribute 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"class\\"/>\`);
@@ -166,10 +141,9 @@ exports[`attributes dynamic class attribute 1`] = `
`;
exports[`attributes dynamic class attribute evaluating to 0 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"class\\"/>\`);
@@ -180,56 +154,10 @@ exports[`attributes dynamic class attribute evaluating to 0 1`] = `
}"
`;
exports[`attributes dynamic class attribute that starts and ends with a space 1`] = `
"function anonymous(app, bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"class\\"/>\`);
return function template(ctx, node, key = \\"\\") {
let attr1 = ctx['c'];
return block1([attr1]);
}
}"
`;
exports[`attributes dynamic class attribute which is only a space 1`] = `
"function anonymous(app, bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"class\\"/>\`);
return function template(ctx, node, key = \\"\\") {
let attr1 = ctx['c'];
return block1([attr1]);
}
}"
`;
exports[`attributes dynamic class attribute with multiple consecutive spaces 1`] = `
"function anonymous(app, bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"class\\"/>\`);
return function template(ctx, node, key = \\"\\") {
let attr1 = ctx['c'];
return block1([attr1]);
}
}"
`;
exports[`attributes dynamic empty class attribute 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"class\\"/>\`);
@@ -241,10 +169,9 @@ exports[`attributes dynamic empty class attribute 1`] = `
`;
exports[`attributes dynamic formatted attributes with a dash 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"aria-label\\"/>\`);
@@ -256,10 +183,9 @@ exports[`attributes dynamic formatted attributes with a dash 1`] = `
`;
exports[`attributes dynamic undefined class attribute 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"class\\"/>\`);
@@ -271,10 +197,9 @@ exports[`attributes dynamic undefined class attribute 1`] = `
`;
exports[`attributes dynamic undefined generic attribute 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"thing\\"/>\`);
@@ -286,10 +211,9 @@ exports[`attributes dynamic undefined generic attribute 1`] = `
`;
exports[`attributes fixed variable 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"foo\\"/>\`);
@@ -301,10 +225,9 @@ exports[`attributes fixed variable 1`] = `
`;
exports[`attributes format expression 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"foo\\"/>\`);
@@ -316,10 +239,9 @@ exports[`attributes format expression 1`] = `
`;
exports[`attributes format literal 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"foo\\"/>\`);
@@ -331,10 +253,9 @@ exports[`attributes format literal 1`] = `
`;
exports[`attributes format multiple 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"foo\\"/>\`);
@@ -346,10 +267,9 @@ exports[`attributes format multiple 1`] = `
`;
exports[`attributes format value 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"foo\\"/>\`);
@@ -361,10 +281,9 @@ exports[`attributes format value 1`] = `
`;
exports[`attributes from object variables set previously 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<div><span block-attribute-0=\\"class\\"/></div>\`);
@@ -380,10 +299,9 @@ exports[`attributes from object variables set previously 1`] = `
`;
exports[`attributes from variables set previously (no external node) 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<span block-attribute-0=\\"class\\"/>\`);
@@ -399,10 +317,9 @@ exports[`attributes from variables set previously (no external node) 1`] = `
`;
exports[`attributes from variables set previously 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<div><span block-attribute-0=\\"class\\"/></div>\`);
@@ -418,10 +335,9 @@ exports[`attributes from variables set previously 1`] = `
`;
exports[`attributes object 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attributes=\\"0\\"/>\`);
@@ -433,10 +349,9 @@ exports[`attributes object 1`] = `
`;
exports[`attributes static attributes 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div foo=\\"a\\" bar=\\"b\\" baz=\\"c\\"/>\`);
@@ -447,10 +362,9 @@ exports[`attributes static attributes 1`] = `
`;
exports[`attributes static attributes on void elements 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<img src=\\"/test.skip.jpg\\" alt=\\"Test\\"/>\`);
@@ -460,25 +374,10 @@ exports[`attributes static attributes on void elements 1`] = `
}"
`;
exports[`attributes static attributes with backticks 1`] = `
"function anonymous(app, bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<div foo=\\"\\\\\`bar\\\\\`\\"/>\`);
return function template(ctx, node, key = \\"\\") {
return block1();
}
}"
`;
exports[`attributes static attributes with dashes 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div aria-label=\\"Close\\"/>\`);
@@ -488,26 +387,10 @@ exports[`attributes static attributes with dashes 1`] = `
}"
`;
exports[`attributes string interpolation, alternate syntax 1`] = `
"function anonymous(app, bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"foo\\"/>\`);
return function template(ctx, node, key = \\"\\") {
let attr1 = \`b\${ctx['value']}r\`;
return block1([attr1]);
}
}"
`;
exports[`attributes t-att-class and class should combine together 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div class=\\"hello\\" block-attribute-0=\\"class\\"/>\`);
@@ -519,10 +402,9 @@ exports[`attributes t-att-class and class should combine together 1`] = `
`;
exports[`attributes t-att-class with multiple classes 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"class\\"/>\`);
@@ -534,10 +416,9 @@ exports[`attributes t-att-class with multiple classes 1`] = `
`;
exports[`attributes t-att-class with multiple classes 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"class\\"/>\`);
@@ -548,26 +429,10 @@ exports[`attributes t-att-class with multiple classes 2`] = `
}"
`;
exports[`attributes t-att-class with multiple classes 3`] = `
"function anonymous(app, bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"class\\"/>\`);
return function template(ctx, node, key = \\"\\") {
let attr1 = {'a b c':ctx['value']};
return block1([attr1]);
}
}"
`;
exports[`attributes t-att-class with multiple classes, some of which are duplicate 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"class\\"/>\`);
@@ -579,10 +444,9 @@ exports[`attributes t-att-class with multiple classes, some of which are duplica
`;
exports[`attributes t-att-class with object 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div class=\\"static\\" block-attribute-0=\\"class\\"/>\`);
@@ -593,41 +457,10 @@ exports[`attributes t-att-class with object 1`] = `
}"
`;
exports[`attributes t-att-class with object 2`] = `
"function anonymous(app, bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"class\\"/>\`);
return function template(ctx, node, key = \\"\\") {
let attr1 = {' a ':ctx['value']};
return block1([attr1]);
}
}"
`;
exports[`attributes t-att-class with object 3`] = `
"function anonymous(app, bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"class\\"/>\`);
return function template(ctx, node, key = \\"\\") {
let attr1 = {' ':ctx['value']};
return block1([attr1]);
}
}"
`;
exports[`attributes t-attf-class 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"class\\"/>\`);
@@ -639,10 +472,9 @@ exports[`attributes t-attf-class 1`] = `
`;
exports[`attributes t-attf-class should combine with class 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div class=\\"hello\\" block-attribute-0=\\"class\\"/>\`);
@@ -654,10 +486,9 @@ exports[`attributes t-attf-class should combine with class 1`] = `
`;
exports[`attributes t-attf-class with multiple classes 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"class\\"/>\`);
@@ -669,10 +500,9 @@ exports[`attributes t-attf-class with multiple classes 1`] = `
`;
exports[`attributes t-attf-class with multiple classes separated by multiple spaces 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"class\\"/>\`);
@@ -684,10 +514,9 @@ exports[`attributes t-attf-class with multiple classes separated by multiple spa
`;
exports[`attributes tuple literal 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attributes=\\"0\\"/>\`);
@@ -699,10 +528,9 @@ exports[`attributes tuple literal 1`] = `
`;
exports[`attributes tuple variable 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attributes=\\"0\\"/>\`);
@@ -714,10 +542,9 @@ exports[`attributes tuple variable 1`] = `
`;
exports[`attributes two classes 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div class=\\"a b\\"/>\`);
@@ -728,10 +555,9 @@ exports[`attributes two classes 1`] = `
`;
exports[`attributes two dynamic attributes 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-attribute-0=\\"foo\\" block-attribute-1=\\"bar\\"/>\`);
@@ -744,10 +570,9 @@ exports[`attributes two dynamic attributes 1`] = `
`;
exports[`attributes updating classes (with obj notation) 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div class=\\"hoy\\" block-attribute-0=\\"class\\"/>\`);
@@ -759,10 +584,9 @@ exports[`attributes updating classes (with obj notation) 1`] = `
`;
exports[`attributes various escapes 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div foo=\\"&lt;foo\\" block-attribute-0=\\"bar\\" block-attribute-1=\\"baz\\" block-attributes=\\"2\\"/>\`);
@@ -776,10 +600,9 @@ exports[`attributes various escapes 1`] = `
`;
exports[`attributes various escapes 2 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div> &lt; </div>\`);
@@ -790,10 +613,9 @@ exports[`attributes various escapes 2 1`] = `
`;
exports[`special cases for some specific html attributes/properties input of type checkbox with t-att-indeterminate 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<input type=\\"checkbox\\" block-attribute-0=\\"indeterminate\\"/>\`);
@@ -805,10 +627,9 @@ exports[`special cases for some specific html attributes/properties input of typ
`;
exports[`special cases for some specific html attributes/properties input type= checkbox, with t-att-checked 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<input type=\\"checkbox\\" block-attribute-0=\\"checked\\"/>\`);
@@ -820,10 +641,9 @@ exports[`special cases for some specific html attributes/properties input type=
`;
exports[`special cases for some specific html attributes/properties input with t-att-value 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<input block-attribute-0=\\"value\\"/>\`);
@@ -835,10 +655,9 @@ exports[`special cases for some specific html attributes/properties input with t
`;
exports[`special cases for some specific html attributes/properties select with t-att-value 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<select block-attribute-0=\\"value\\"><option value=\\"potato\\">Potato</option><option value=\\"tomato\\">Tomato</option><option value=\\"onion\\">Onion</option></select>\`);
@@ -850,10 +669,9 @@ exports[`special cases for some specific html attributes/properties select with
`;
exports[`special cases for some specific html attributes/properties textarea with t-att-value 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<textarea block-attribute-0=\\"value\\"/>\`);
@@ -865,10 +683,9 @@ exports[`special cases for some specific html attributes/properties textarea wit
`;
exports[`special cases for some specific html attributes/properties various boolean html attributes 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><input type=\\"checkbox\\" checked=\\"checked\\"/><input checked=\\"checked\\"/><div checked=\\"checked\\"/><div selected=\\"selected\\"/><option selected=\\"selected\\" other=\\"1\\"/><input readonly=\\"readonly\\"/><button disabled=\\"disabled\\"/></div>\`);
@@ -1,10 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`comments only a comment 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
return comment(\` comment\`);
@@ -13,10 +12,9 @@ exports[`comments only a comment 1`] = `
`;
exports[`comments properly handle comments 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div>hello <!-- comment-->owl</div>\`);
@@ -27,10 +25,9 @@ exports[`comments properly handle comments 1`] = `
`;
exports[`comments properly handle comments between t-if/t-else 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
let block2 = createBlock(\`<span>true</span>\`);
@@ -1,10 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`t-on can bind event handler 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
@@ -16,10 +15,9 @@ exports[`t-on can bind event handler 1`] = `
`;
exports[`t-on can bind handlers with arguments 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
@@ -32,10 +30,9 @@ exports[`t-on can bind handlers with arguments 1`] = `
`;
exports[`t-on can bind handlers with empty object 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
@@ -48,10 +45,9 @@ exports[`t-on can bind handlers with empty object 1`] = `
`;
exports[`t-on can bind handlers with empty object (with non empty inner string) 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
@@ -64,10 +60,9 @@ exports[`t-on can bind handlers with empty object (with non empty inner string)
`;
exports[`t-on can bind handlers with empty object (with non empty inner string) 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
let block1 = createBlock(\`<ul><block-child-0/></ul>\`);
@@ -75,27 +70,26 @@ exports[`t-on can bind handlers with empty object (with non empty inner string)
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(['someval']);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList(['someval']);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`action\`] = v_block2[i1];
ctx[\`action_index\`] = i1;
const key1 = ctx['action_index'];
let key1 = ctx['action_index'];
const v1 = ctx['activate'];
const v2 = ctx['action'];
let hdlr1 = [()=>v1(v2), ctx];
c_block2[i1] = withKey(block3([hdlr1]), key1);
}
const b2 = list(c_block2);
let b2 = list(c_block2);
return block1([], [b2]);
}
}"
`;
exports[`t-on can bind handlers with object arguments 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
@@ -108,10 +102,9 @@ exports[`t-on can bind handlers with object arguments 1`] = `
`;
exports[`t-on can bind two event handlers 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<button block-handler-0=\\"click\\" block-handler-1=\\"dblclick\\">Click</button>\`);
@@ -124,10 +117,9 @@ exports[`t-on can bind two event handlers 1`] = `
`;
exports[`t-on handler is bound to proper owner 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
@@ -139,20 +131,19 @@ exports[`t-on handler is bound to proper owner 1`] = `
`;
exports[`t-on handler is bound to proper owner, part 2 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
let block2 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block1, v_block1, l_block1, c_block1] = prepareList([1]);;
const [k_block1, v_block1, l_block1, c_block1] = prepareList([1]);
for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`value\`] = v_block1[i1];
const key1 = ctx['value'];
let key1 = ctx['value'];
let hdlr1 = [ctx['add'], ctx];
c_block1[i1] = withKey(block2([hdlr1]), key1);
}
@@ -162,11 +153,11 @@ exports[`t-on handler is bound to proper owner, part 2 1`] = `
`;
exports[`t-on handler is bound to proper owner, part 3 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const callTemplate_1 = app.getTemplate(\`sub\`);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { getTemplate } = helpers;
const callTemplate_1 = getTemplate(\`sub\`);
return function template(ctx, node, key = \\"\\") {
return callTemplate_1.call(this, ctx, node, key + \`__1\`);
@@ -175,10 +166,9 @@ exports[`t-on handler is bound to proper owner, part 3 1`] = `
`;
exports[`t-on handler is bound to proper owner, part 3 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
@@ -190,23 +180,22 @@ exports[`t-on handler is bound to proper owner, part 3 2`] = `
`;
exports[`t-on handler is bound to proper owner, part 4 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { prepareList, withKey } = helpers;
const callTemplate_1 = app.getTemplate(\`sub\`);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, getTemplate, withKey } = helpers;
const callTemplate_1 = getTemplate(\`sub\`);
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block1, v_block1, l_block1, c_block1] = prepareList([1]);;
const [k_block1, v_block1, l_block1, c_block1] = prepareList([1]);
for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`value\`] = v_block1[i1];
ctx[\`value_first\`] = i1 === 0;
ctx[\`value_last\`] = i1 === v_block1.length - 1;
ctx[\`value_index\`] = i1;
ctx[\`value_value\`] = k_block1[i1];
const key1 = ctx['value'];
let key1 = ctx['value'];
c_block1[i1] = withKey(callTemplate_1.call(this, ctx, node, key + \`__1__\${key1}\`), key1);
}
return list(c_block1);
@@ -215,10 +204,9 @@ exports[`t-on handler is bound to proper owner, part 4 1`] = `
`;
exports[`t-on handler is bound to proper owner, part 4 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
@@ -230,10 +218,9 @@ exports[`t-on handler is bound to proper owner, part 4 2`] = `
`;
exports[`t-on receive event in first argument 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
@@ -245,10 +232,9 @@ exports[`t-on receive event in first argument 1`] = `
`;
exports[`t-on t-on modifiers (native listener) basic support for native listener 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div class=\\"myClass\\" block-handler-0=\\"click\\"><button block-handler-1=\\"click\\">Button</button></div>\`);
@@ -261,10 +247,9 @@ exports[`t-on t-on modifiers (native listener) basic support for native listener
`;
exports[`t-on t-on modifiers (native listener) t-on combined with t-esc 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><button block-handler-0=\\"click\\"><block-text-1/></button></div>\`);
@@ -277,27 +262,25 @@ exports[`t-on t-on modifiers (native listener) t-on combined with t-esc 1`] = `
`;
exports[`t-on t-on modifiers (native listener) t-on combined with t-out 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<div><button block-handler-0=\\"click\\"><block-child-0/></button></div>\`);
return function template(ctx, node, key = \\"\\") {
let hdlr1 = [ctx['onClick'], ctx];
const b2 = safeOutput(ctx['html']);
let b2 = safeOutput(ctx['html']);
return block1([hdlr1], [b2]);
}
}"
`;
exports[`t-on t-on modifiers (native listener) t-on with .capture modifier 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-handler-0=\\"click.capture\\"><button block-handler-1=\\"click\\">Button</button></div>\`);
@@ -310,10 +293,9 @@ exports[`t-on t-on modifiers (native listener) t-on with .capture modifier 1`] =
`;
exports[`t-on t-on modifiers (native listener) t-on with empty handler (only modifiers) 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><button block-handler-0=\\"click.prevent\\">Button</button></div>\`);
@@ -325,10 +307,9 @@ exports[`t-on t-on modifiers (native listener) t-on with empty handler (only mod
`;
exports[`t-on t-on modifiers (native listener) t-on with prevent and self modifiers (order matters) 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><button block-handler-0=\\"click.prevent.self\\"><span>Button</span></button></div>\`);
@@ -340,10 +321,9 @@ exports[`t-on t-on modifiers (native listener) t-on with prevent and self modifi
`;
exports[`t-on t-on modifiers (native listener) t-on with prevent and/or stop modifiers 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><button block-handler-0=\\"click.prevent\\">Button 1</button><button block-handler-1=\\"click.stop\\">Button 2</button><button block-handler-2=\\"click.prevent.stop\\">Button 3</button></div>\`);
@@ -357,10 +337,9 @@ exports[`t-on t-on modifiers (native listener) t-on with prevent and/or stop mod
`;
exports[`t-on t-on modifiers (native listener) t-on with prevent modifier in t-foreach 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
@@ -368,27 +347,26 @@ exports[`t-on t-on modifiers (native listener) t-on with prevent modifier in t-f
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['projects']);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['projects']);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`project\`] = v_block2[i1];
const key1 = ctx['project'];
let key1 = ctx['project'];
const v1 = ctx['onEdit'];
const v2 = ctx['project'];
let hdlr1 = [\\"prevent\\", _ev=>v1(v2.id,_ev), ctx];
let txt1 = ctx['project'].name;
c_block2[i1] = withKey(block3([hdlr1, txt1]), key1);
}
const b2 = list(c_block2);
let b2 = list(c_block2);
return block1([], [b2]);
}
}"
`;
exports[`t-on t-on modifiers (native listener) t-on with self and prevent modifiers (order matters) 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><button block-handler-0=\\"click.self.prevent\\"><span>Button</span></button></div>\`);
@@ -400,10 +378,9 @@ exports[`t-on t-on modifiers (native listener) t-on with self and prevent modifi
`;
exports[`t-on t-on modifiers (native listener) t-on with self modifier 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><button block-handler-0=\\"click\\"><span>Button</span></button><button block-handler-1=\\"click.self\\"><span>Button</span></button></div>\`);
@@ -416,10 +393,9 @@ exports[`t-on t-on modifiers (native listener) t-on with self modifier 1`] = `
`;
exports[`t-on t-on modifiers (synthetic listener) basic support for synthetic 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div block-handler-0=\\"click.synthetic\\"><button block-handler-1=\\"click.synthetic\\">Button</button></div>\`);
@@ -432,10 +408,9 @@ exports[`t-on t-on modifiers (synthetic listener) basic support for synthetic 1`
`;
exports[`t-on t-on with inline statement (function call) 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
@@ -448,10 +423,9 @@ exports[`t-on t-on with inline statement (function call) 1`] = `
`;
exports[`t-on t-on with inline statement 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Click</button>\`);
@@ -464,10 +438,9 @@ exports[`t-on t-on with inline statement 1`] = `
`;
exports[`t-on t-on with inline statement, part 2 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Toggle</button>\`);
@@ -480,10 +453,9 @@ exports[`t-on t-on with inline statement, part 2 1`] = `
`;
exports[`t-on t-on with inline statement, part 3 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<button block-handler-0=\\"click\\">Toggle</button>\`);
@@ -497,26 +469,25 @@ exports[`t-on t-on with inline statement, part 3 1`] = `
`;
exports[`t-on t-on with t-call 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const callTemplate_1 = app.getTemplate(\`sub\`);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { getTemplate } = helpers;
const callTemplate_1 = getTemplate(\`sub\`);
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
let b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
return block1([], [b2]);
}
}"
`;
exports[`t-on t-on with t-call 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<p block-handler-0=\\"click\\">lucas</p>\`);
@@ -528,26 +499,25 @@ exports[`t-on t-on with t-call 2`] = `
`;
exports[`t-on t-on, with arguments and t-call 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const callTemplate_1 = app.getTemplate(\`sub\`);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { getTemplate } = helpers;
const callTemplate_1 = getTemplate(\`sub\`);
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
let b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
return block1([], [b2]);
}
}"
`;
exports[`t-on t-on, with arguments and t-call 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<p block-handler-0=\\"click\\">lucas</p>\`);
+55 -64
View File
@@ -1,12 +1,10 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`misc complex template 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
const comp1 = app.createComponent(\`SlotButton\`, true, false, false, false);
let block1 = createBlock(\`<div block-attribute-0=\\"class\\"><div block-attribute-1=\\"class\\"><div class=\\"batch_header\\"><a block-attribute-2=\\"href\\" block-attribute-3=\\"class\\" title=\\"View Batch\\"><block-text-4/><block-child-0/><i class=\\"arrow fa fa-window-maximize\\"/></a></div><block-child-1/><div class=\\"batch_slots\\"><block-child-2/><block-child-3/></div><div class=\\"batch_commits\\"><block-child-4/></div></div></div>\`);
let block2 = createBlock(\`<i class=\\"fa fa-exclamation-triangle\\"/>\`);
@@ -32,28 +30,28 @@ exports[`misc complex template 1`] = `
b3 = block3();
}
ctx = Object.create(ctx);
const [k_block4, v_block4, l_block4, c_block4] = prepareList(ctx['batch'].slot_ids.filter(_slot=>_slot.build_id.id&&!_slot.trigger_id.manual&&(ctx['options'].trigger_display[_slot.trigger_id.id])));;
const [k_block4, v_block4, l_block4, c_block4] = prepareList(ctx['batch'].slot_ids.filter(_slot=>_slot.build_id.id&&!_slot.trigger_id.manual&&(ctx['options'].trigger_display[_slot.trigger_id.id])));
for (let i1 = 0; i1 < l_block4; i1++) {
ctx[\`slot\`] = v_block4[i1];
const key1 = ctx['slot'].id;
c_block4[i1] = withKey(comp1({class: ctx['slot_container'],slot: ctx['slot']}, key + \`__1__\${key1}\`, node, this, null), key1);
let key1 = ctx['slot'].id;
c_block4[i1] = withKey(component(\`SlotButton\`, {class: ctx['slot_container'], slot: ctx['slot']}, key + \`__1__\${key1}\`, node, ctx), key1);
}
ctx = ctx.__proto__;
b4 = list(c_block4);
ctx = Object.create(ctx);
const [k_block6, v_block6, l_block6, c_block6] = prepareList([1,2,3,4]);;
const [k_block6, v_block6, l_block6, c_block6] = prepareList([1,2,3,4]);
for (let i1 = 0; i1 < l_block6; i1++) {
ctx[\`x\`] = v_block6[i1];
const key1 = ctx['x'];
let key1 = ctx['x'];
c_block6[i1] = withKey(block7(), key1);
}
ctx = ctx.__proto__;
b6 = list(c_block6);
ctx = Object.create(ctx);
const [k_block8, v_block8, l_block8, c_block8] = prepareList(ctx['commit_links']);;
const [k_block8, v_block8, l_block8, c_block8] = prepareList(ctx['commit_links']);
for (let i1 = 0; i1 < l_block8; i1++) {
ctx[\`commit_link\`] = v_block8[i1];
const key1 = ctx['commit_link'].id;
let key1 = ctx['commit_link'].id;
let b10,b11,b12,b13;
let attr5 = \`/runbot/commit/\${ctx['commit_link'].commit_id}\`;
let attr6 = \`badge badge-light batch_commit match_type_\${ctx['commit_link'].match_type}\`;
@@ -81,16 +79,15 @@ exports[`misc complex template 1`] = `
`;
exports[`misc global 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { prepareList, isBoundary, withDefault, setContextValue, zero, withKey } = helpers;
const callTemplate_1 = app.getTemplate(\`_callee-uses-foo\`);
const callTemplate_2 = app.getTemplate(\`_callee-uses-foo\`);
const callTemplate_3 = app.getTemplate(\`_callee-uses-foo\`);
const callTemplate_4 = app.getTemplate(\`_callee-asc\`);
const callTemplate_5 = app.getTemplate(\`_callee-asc-toto\`);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, isBoundary, withDefault, setContextValue, getTemplate, zero, withKey } = helpers;
const callTemplate_1 = getTemplate(\`_callee-uses-foo\`);
const callTemplate_2 = getTemplate(\`_callee-uses-foo\`);
const callTemplate_3 = getTemplate(\`_callee-uses-foo\`);
const callTemplate_4 = getTemplate(\`_callee-asc\`);
const callTemplate_5 = getTemplate(\`_callee-asc-toto\`);
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
let block4 = createBlock(\`<span><block-text-0/></span>\`);
@@ -99,45 +96,44 @@ exports[`misc global 1`] = `
ctx = Object.create(ctx);
ctx[isBoundary] = 1
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList([4,5,6]);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList([4,5,6]);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`value\`] = v_block2[i1];
ctx[\`value_first\`] = i1 === 0;
ctx[\`value_last\`] = i1 === v_block2.length - 1;
ctx[\`value_index\`] = i1;
ctx[\`value_value\`] = k_block2[i1];
const key1 = ctx['value'];
let key1 = ctx['value'];
let txt1 = ctx['value'];
const b4 = block4([txt1]);
let b4 = block4([txt1]);
ctx = Object.create(ctx);
ctx[isBoundary] = 1;
ctx = Object.create(ctx);
ctx[isBoundary] = 1;
setContextValue(ctx, \\"foo\\", 'aaa');
const b6 = callTemplate_1.call(this, ctx, node, key + \`__1__\${key1}\`);
let b6 = callTemplate_1.call(this, ctx, node, key + \`__1__\${key1}\`);
ctx = ctx.__proto__;
const b7 = callTemplate_2.call(this, ctx, node, key + \`__2__\${key1}\`);
let b7 = callTemplate_2.call(this, ctx, node, key + \`__2__\${key1}\`);
setContextValue(ctx, \\"foo\\", 'bbb');
const b8 = callTemplate_3.call(this, ctx, node, key + \`__3__\${key1}\`);
const b5 = multi([b6, b7, b8]);
let b8 = callTemplate_3.call(this, ctx, node, key + \`__3__\${key1}\`);
let b5 = multi([b6, b7, b8]);
ctx[zero] = b5;
const b9 = callTemplate_4.call(this, ctx, node, key + \`__4__\${key1}\`);
let b9 = callTemplate_4.call(this, ctx, node, key + \`__4__\${key1}\`);
ctx = ctx.__proto__;
c_block2[i1] = withKey(multi([b4, b9]), key1);
}
ctx = ctx.__proto__;
const b2 = list(c_block2);
const b10 = callTemplate_5.call(this, ctx, node, key + \`__5\`);
let b2 = list(c_block2);
let b10 = callTemplate_5.call(this, ctx, node, key + \`__5\`);
return block1([], [b2, b10]);
}
}"
`;
exports[`misc global 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { withDefault } = helpers;
let block1 = createBlock(\`<span><block-text-0/></span>\`);
@@ -150,48 +146,43 @@ exports[`misc global 2`] = `
`;
exports[`misc global 3`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { zero } = helpers;
let block1 = createBlock(\`<año block-attribute-0=\\"falló\\"><block-child-0/></año>\`);
return function template(ctx, node, key = \\"\\") {
let attr1 = 'agüero';
const b2 = ctx[zero];
let b2 = ctx[zero];
return block1([attr1], [b2]);
}
}"
`;
exports[`misc global 4`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput, withDefault } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const b3 = text(\`toto default\`);
const b2 = withDefault(safeOutput(ctx['toto']), b3);
let b3 = text(\`toto default\`);
let b2 = withDefault(safeOutput(ctx['toto']), b3);
return block1([], [b2]);
}
}"
`;
exports[`misc other complex template 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { prepareList, withKey } = helpers;
const callTemplate_1 = app.getTemplate(\`LOAD_INFOS_TEMPLATE\`);
const comp1 = app.createComponent(\`BundlesList\`, true, false, false, false);
const comp2 = app.createComponent(\`BundlesList\`, true, false, false, false);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey, getTemplate } = helpers;
const callTemplate_1 = getTemplate(\`LOAD_INFOS_TEMPLATE\`);
let block1 = createBlock(\`<div><header><nav class=\\"navbar navbar-expand-md navbar-light bg-light\\"><a block-attribute-0=\\"href\\"><b style=\\"color:#777;\\"><block-text-1/></b></a><button type=\\"button\\" class=\\"navbar-toggler\\" data-toggle=\\"collapse\\" data-target=\\"#top_menu_collapse\\"><span class=\\"navbar-toggler-icon\\"/></button><div class=\\"collapse navbar-collapse\\" id=\\"top_menu_collapse\\" aria-expanded=\\"false\\"><ul class=\\"nav navbar-nav ml-auto text-right\\" id=\\"top_menu\\"><block-child-0/><li class=\\"nav-item divider\\"/><block-child-1/></ul><div><div class=\\"input-group input-group-sm\\"><div class=\\"input-group-prepend input-group-sm\\"><button class=\\"btn btn-default fa fa-cog\\" title=\\"Settings\\" block-handler-2=\\"click\\"/><button class=\\"btn btn-default\\" block-handler-3=\\"click\\"> More </button><block-child-2/></div><input class=\\"form-control\\" type=\\"text\\" placeholder=\\"Search\\" aria-label=\\"Search\\" name=\\"search\\" block-attribute-4=\\"value\\" block-handler-5=\\"keyup\\" block-handler-6=\\"change\\" block-ref=\\"7\\"/><div class=\\"input-group-append\\"><button class=\\"btn btn-default fa fa-eraser\\" block-handler-8=\\"click\\"/></div></div></div></div></nav></header><div class=\\"container-fluid\\" block-ref=\\"9\\"><div class=\\"row\\"><!--div class=\\"form-group col-md-6\\">
<h5>Search options</h5>
@@ -230,10 +221,10 @@ exports[`misc other complex template 1`] = `
let attr1 = \`/runbot/\${ctx['project'].slug}\`;
let txt1 = ctx['project'].name;
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['projects']);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['projects']);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`project\`] = v_block2[i1];
const key1 = ctx['project'].id;
let key1 = ctx['project'].id;
let hdlr1 = [ctx['selectProject'](ctx['project']), ctx];
let txt2 = ctx['project'].name;
c_block2[i1] = withKey(block3([hdlr1, txt2]), key1);
@@ -250,12 +241,12 @@ exports[`misc other complex template 1`] = `
if (ctx['nb_assigned_errors']&&ctx['nb_assigned_errors']>0) {
let attr3 = \`You have \${ctx['nb_assigned_errors']} random bug assigned\`;
let txt3 = ctx['nb_assigned_errors'];
const b8 = block8([attr3, txt3]);
const b9 = block9();
let b8 = block8([attr3, txt3]);
let b9 = block9();
b7 = multi([b8, b9]);
} else if (ctx['nb_build_errors']&&ctx['nb_build_errors']>0) {
const b11 = block11();
const b12 = block12();
let b11 = block11();
let b12 = block12();
b10 = multi([b11, b12]);
}
let txt4 = ctx['user'].name.length>25?ctx['user'].namesubstring(0,23)+'...':ctx['user'].name;
@@ -270,17 +261,17 @@ exports[`misc other complex template 1`] = `
let hdlr3 = [ctx['toggleMore'], ctx];
if (ctx['categories']&&ctx['categories'].length>1) {
ctx = Object.create(ctx);
const [k_block15, v_block15, l_block15, c_block15] = prepareList(ctx['categories']);;
const [k_block15, v_block15, l_block15, c_block15] = prepareList(ctx['categories']);
for (let i1 = 0; i1 < l_block15; i1++) {
ctx[\`category\`] = v_block15[i1];
const key1 = ctx['category'].id;
let key1 = ctx['category'].id;
let attr6 = ctx['category'].id;
let attr7 = ctx['category'].id==ctx['options'].active_category_id;
let txt5 = ctx['category'].name;
c_block15[i1] = withKey(block16([attr6, attr7, txt5]), key1);
}
ctx = ctx.__proto__;
const b15 = list(c_block15);
let b15 = list(c_block15);
b14 = block14([], [b15]);
}
let attr8 = ctx['search'].value;
@@ -289,10 +280,10 @@ exports[`misc other complex template 1`] = `
let hdlr6 = [ctx['clearSearch'], ctx];
if (ctx['triggers']) {
ctx = Object.create(ctx);
const [k_block18, v_block18, l_block18, c_block18] = prepareList(ctx['triggers']);;
const [k_block18, v_block18, l_block18, c_block18] = prepareList(ctx['triggers']);
for (let i1 = 0; i1 < l_block18; i1++) {
ctx[\`trigger\`] = v_block18[i1];
const key1 = ctx['trigger'].id;
let key1 = ctx['trigger'].id;
let b20;
if (!ctx['trigger'].manual&&ctx['trigger'].project_id===ctx['project'].id&&ctx['trigger'].category_id===ctx['options'].active_category_id) {
let attr9 = \`trigger_\${ctx['trigger'].id}\`;
@@ -307,12 +298,12 @@ exports[`misc other complex template 1`] = `
c_block18[i1] = withKey(multi([b20]), key1);
}
ctx = ctx.__proto__;
const b18 = list(c_block18);
let b18 = list(c_block18);
let hdlr8 = [ctx['triggerAll'], ctx];
let hdlr9 = [ctx['triggerNone'], ctx];
let hdlr10 = [ctx['triggerDefault'], ctx];
let hdlr11 = [ctx['toggleSettingsMenu'], ctx];
const b21 = block21([hdlr8, hdlr9, hdlr10, hdlr11]);
let b21 = block21([hdlr8, hdlr9, hdlr10, hdlr11]);
b17 = multi([b18, b21]);
}
if (ctx['load_infos']) {
@@ -325,8 +316,8 @@ exports[`misc other complex template 1`] = `
if (!ctx['project']) {
b24 = block24();
} else {
const b26 = comp1({bundles: ctx['bundles'].sticky,category_custom_views: ctx['category_custom_views'],search: ctx['search']}, key + \`__2\`, node, this, null);
const b27 = comp2({bundles: ctx['bundles'].dev,search: ctx['search']}, key + \`__3\`, node, this, null);
let b26 = component(\`BundlesList\`, {bundles: ctx['bundles'].sticky, category_custom_views: ctx['category_custom_views'], search: ctx['search']}, key + \`__2\`, node, ctx);
let b27 = component(\`BundlesList\`, {bundles: ctx['bundles'].dev, search: ctx['search']}, key + \`__3\`, node, ctx);
b25 = block25([], [b26, b27]);
}
return block1([attr1, txt1, hdlr2, hdlr3, attr8, hdlr4, hdlr5, ref1, hdlr6, ref2], [b2, b4, b14, b17, b22, b23, b24, b25]);
@@ -1,24 +1,23 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`memory t-foreach does not leak stuff in global scope 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
let block1 = createBlock(\`<p><block-child-0/></p>\`);
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList([3,2,1]);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList([3,2,1]);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`item\`] = v_block2[i1];
ctx[\`item_index\`] = i1;
const key1 = ctx['item_index'];
let key1 = ctx['item_index'];
c_block2[i1] = withKey(text(ctx['item']), key1);
}
const b2 = list(c_block2);
let b2 = list(c_block2);
return block1([], [b2]);
}
}"
@@ -1,10 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`simple templates, mostly static can render a table row 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<tr><td>cell</td></tr>\`);
@@ -15,10 +14,9 @@ exports[`simple templates, mostly static can render a table row 1`] = `
`;
exports[`simple templates, mostly static div with a class attribute 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div class=\\"abc\\">foo</div>\`);
@@ -29,10 +27,9 @@ exports[`simple templates, mostly static div with a class attribute 1`] = `
`;
exports[`simple templates, mostly static div with a class attribute with a quote 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div class=\\"a'bc\\">word</div>\`);
@@ -43,10 +40,9 @@ exports[`simple templates, mostly static div with a class attribute with a quote
`;
exports[`simple templates, mostly static div with a span child node 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><span>word</span></div>\`);
@@ -57,10 +53,9 @@ exports[`simple templates, mostly static div with a span child node 1`] = `
`;
exports[`simple templates, mostly static div with an arbitrary attribute with a quote 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div abc=\\"a'bc\\">word</div>\`);
@@ -71,10 +66,9 @@ exports[`simple templates, mostly static div with an arbitrary attribute with a
`;
exports[`simple templates, mostly static div with an empty class attribute 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div>word</div>\`);
@@ -85,10 +79,9 @@ exports[`simple templates, mostly static div with an empty class attribute 1`] =
`;
exports[`simple templates, mostly static div with content 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div>foo</div>\`);
@@ -99,10 +92,9 @@ exports[`simple templates, mostly static div with content 1`] = `
`;
exports[`simple templates, mostly static dom node with t-esc 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -114,10 +106,9 @@ exports[`simple templates, mostly static dom node with t-esc 1`] = `
`;
exports[`simple templates, mostly static dom node with t-esc 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -129,10 +120,9 @@ exports[`simple templates, mostly static dom node with t-esc 2`] = `
`;
exports[`simple templates, mostly static dynamic text value 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
return text(ctx['text']);
@@ -141,10 +131,9 @@ exports[`simple templates, mostly static dynamic text value 1`] = `
`;
exports[`simple templates, mostly static empty div 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div/>\`);
@@ -155,10 +144,9 @@ exports[`simple templates, mostly static empty div 1`] = `
`;
exports[`simple templates, mostly static empty string 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
return text(\`\`);
@@ -167,10 +155,9 @@ exports[`simple templates, mostly static empty string 1`] = `
`;
exports[`simple templates, mostly static empty string in a template set 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
return text(\`\`);
@@ -179,10 +166,9 @@ exports[`simple templates, mostly static empty string in a template set 1`] = `
`;
exports[`simple templates, mostly static inline template string in t-esc 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
return text(\`text\`);
@@ -191,10 +177,9 @@ exports[`simple templates, mostly static inline template string in t-esc 1`] = `
`;
exports[`simple templates, mostly static inline template string with content in t-esc 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
return function template(ctx, node, key = \\"\\") {
@@ -207,10 +192,9 @@ exports[`simple templates, mostly static inline template string with content in
`;
exports[`simple templates, mostly static inline template string with variable in context 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
return text(\`text \${ctx['v']}\`);
@@ -219,27 +203,25 @@ exports[`simple templates, mostly static inline template string with variable in
`;
exports[`simple templates, mostly static multiple root nodes 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block2 = createBlock(\`<div>foo</div>\`);
let block3 = createBlock(\`<span>hey</span>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = block2();
const b3 = block3();
let b2 = block2();
let b3 = block3();
return multi([b2, b3]);
}
}"
`;
exports[`simple templates, mostly static simple string 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
return text(\`hello vdom\`);
@@ -248,10 +230,9 @@ exports[`simple templates, mostly static simple string 1`] = `
`;
exports[`simple templates, mostly static simple string in t tag 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
return text(\`hello vdom\`);
@@ -260,38 +241,35 @@ exports[`simple templates, mostly static simple string in t tag 1`] = `
`;
exports[`simple templates, mostly static static text and dynamic text (no t tag) 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
const b2 = text(\`hello \`);
const b3 = text(ctx['text']);
let b2 = text(\`hello \`);
let b3 = text(ctx['text']);
return multi([b2, b3]);
}
}"
`;
exports[`simple templates, mostly static static text and dynamic text 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
const b2 = text(\`hello \`);
const b3 = text(ctx['text']);
let b2 = text(\`hello \`);
let b3 = text(ctx['text']);
return multi([b2, b3]);
}
}"
`;
exports[`simple templates, mostly static t-esc in dom node 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -303,10 +281,9 @@ exports[`simple templates, mostly static t-esc in dom node 1`] = `
`;
exports[`simple templates, mostly static t-esc in dom node, variations 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div>hello <block-text-0/></div>\`);
@@ -318,10 +295,9 @@ exports[`simple templates, mostly static t-esc in dom node, variations 1`] = `
`;
exports[`simple templates, mostly static t-esc in dom node, variations 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div>hello <block-text-0/> world</div>\`);
@@ -333,10 +309,9 @@ exports[`simple templates, mostly static t-esc in dom node, variations 2`] = `
`;
exports[`simple templates, mostly static template with multiple t tag with multiple content 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-text-0/><block-text-1/>Loading<block-text-2/></div>\`);
@@ -350,10 +325,9 @@ exports[`simple templates, mostly static template with multiple t tag with multi
`;
exports[`simple templates, mostly static template with t tag with multiple content 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div>Loading<block-child-0/></div>\`);
@@ -368,38 +342,35 @@ exports[`simple templates, mostly static template with t tag with multiple conte
`;
exports[`simple templates, mostly static two t-escs next to each other 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
const b2 = text(ctx['text1']);
const b3 = text(ctx['text2']);
let b2 = text(ctx['text1']);
let b3 = text(ctx['text2']);
return multi([b2, b3]);
}
}"
`;
exports[`simple templates, mostly static two t-escs next to each other 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
const b2 = text(ctx['text1']);
const b3 = text(ctx['text2']);
let b2 = text(ctx['text1']);
let b3 = text(ctx['text2']);
return multi([b2, b3]);
}
}"
`;
exports[`simple templates, mostly static two t-escs next to each other, in a div 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-text-0/><block-text-1/></div>\`);
+23 -31
View File
@@ -1,10 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`properly support svg add proper namespace to g tags 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<g block-ns=\\"http://www.w3.org/2000/svg\\"><circle cx=\\"50\\" cy=\\"50\\" r=\\"4\\" stroke=\\"green\\" stroke-width=\\"1\\" fill=\\"yellow\\"/> </g>\`);
@@ -15,10 +14,9 @@ exports[`properly support svg add proper namespace to g tags 1`] = `
`;
exports[`properly support svg add proper namespace to svg 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\" width=\\"100px\\" height=\\"90px\\"><circle cx=\\"50\\" cy=\\"50\\" r=\\"4\\" stroke=\\"green\\" stroke-width=\\"1\\" fill=\\"yellow\\"/> </svg>\`);
@@ -29,10 +27,9 @@ exports[`properly support svg add proper namespace to svg 1`] = `
`;
exports[`properly support svg namespace to g tags not added if already in svg namespace 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><g/></svg>\`);
@@ -43,10 +40,9 @@ exports[`properly support svg namespace to g tags not added if already in svg na
`;
exports[`properly support svg namespace to svg tags added even if already in svg namespace 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><svg/></svg>\`);
@@ -57,10 +53,9 @@ exports[`properly support svg namespace to svg tags added even if already in svg
`;
exports[`properly support svg svg creates new block if it is within html -- 2 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
let block2 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><polygon fill=\\"#000000\\" points=\\"0 0 4 4 8 0\\" transform=\\"translate(5 7)\\"/><block-child-0/></svg>\`);
@@ -71,49 +66,47 @@ exports[`properly support svg svg creates new block if it is within html -- 2 1`
if (ctx['hasPath']) {
b3 = block3();
}
const b2 = block2([], [b3]);
let b2 = block2([], [b3]);
return block1([], [b2]);
}
}"
`;
exports[`properly support svg svg creates new block if it is within html 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
let block2 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><polygon fill=\\"#000000\\" points=\\"0 0 4 4 8 0\\" transform=\\"translate(5 7)\\"/></svg>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = block2();
let b2 = block2();
return block1([], [b2]);
}
}"
`;
exports[`properly support svg svg namespace added to sub templates if root tag is path 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const callTemplate_1 = app.getTemplate(\`path\`);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { getTemplate } = helpers;
const callTemplate_1 = getTemplate(\`path\`);
let block1 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><block-child-0/></svg>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
let b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
return block1([], [b2]);
}
}"
`;
exports[`properly support svg svg namespace added to sub templates if root tag is path 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<path block-ns=\\"http://www.w3.org/2000/svg\\"/>\`);
@@ -124,10 +117,9 @@ exports[`properly support svg svg namespace added to sub templates if root tag i
`;
exports[`properly support svg svg namespace added to sub-blocks 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<svg block-ns=\\"http://www.w3.org/2000/svg\\"><block-child-0/></svg>\`);
let block2 = createBlock(\`<path block-ns=\\"http://www.w3.org/2000/svg\\"/>\`);
File diff suppressed because it is too large Load Diff
@@ -1,10 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`debugging t-debug 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
let block2 = createBlock(\`<span>hey</span>\`);
@@ -22,10 +21,9 @@ exports[`debugging t-debug 1`] = `
`;
exports[`debugging t-debug on sub template 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<p>coucou</p>\`);
@@ -37,26 +35,25 @@ exports[`debugging t-debug on sub template 1`] = `
`;
exports[`debugging t-debug on sub template 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const callTemplate_1 = app.getTemplate(\`sub\`);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { getTemplate } = helpers;
const callTemplate_1 = getTemplate(\`sub\`);
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
let b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
return block1([], [b2]);
}
}"
`;
exports[`debugging t-log 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<div/>\`);
+38 -50
View File
@@ -1,10 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`t-esc div with falsy values 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><p><block-text-0/></p><p><block-text-1/></p><p><block-text-2/></p><p><block-text-3/></p><p><block-text-4/></p></div>\`);
@@ -20,10 +19,9 @@ exports[`t-esc div with falsy values 1`] = `
`;
exports[`t-esc escaping 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<span><block-text-0/></span>\`);
@@ -35,10 +33,9 @@ exports[`t-esc escaping 1`] = `
`;
exports[`t-esc escaping on a node 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<span><block-text-0/></span>\`);
@@ -50,10 +47,9 @@ exports[`t-esc escaping on a node 1`] = `
`;
exports[`t-esc escaping on a node with a body 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { withDefault } = helpers;
let block1 = createBlock(\`<span><block-text-0/></span>\`);
@@ -66,10 +62,9 @@ exports[`t-esc escaping on a node with a body 1`] = `
`;
exports[`t-esc escaping on a node with a body, as a default 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { withDefault } = helpers;
let block1 = createBlock(\`<span><block-text-0/></span>\`);
@@ -82,31 +77,29 @@ exports[`t-esc escaping on a node with a body, as a default 1`] = `
`;
exports[`t-esc falsy values in text nodes 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
const b2 = text(ctx['v1']);
const b3 = text(\`:\`);
const b4 = text(ctx['v2']);
const b5 = text(\`:\`);
const b6 = text(ctx['v3']);
const b7 = text(\`:\`);
const b8 = text(ctx['v4']);
const b9 = text(\`:\`);
const b10 = text(ctx['v5']);
let b2 = text(ctx['v1']);
let b3 = text(\`:\`);
let b4 = text(ctx['v2']);
let b5 = text(\`:\`);
let b6 = text(ctx['v3']);
let b7 = text(\`:\`);
let b8 = text(ctx['v4']);
let b9 = text(\`:\`);
let b10 = text(ctx['v5']);
return multi([b2, b3, b4, b5, b6, b7, b8, b9, b10]);
}
}"
`;
exports[`t-esc literal 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<span><block-text-0/></span>\`);
@@ -118,10 +111,9 @@ exports[`t-esc literal 1`] = `
`;
exports[`t-esc t-esc is escaped 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, LazyValue } = helpers;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -134,7 +126,7 @@ exports[`t-esc t-esc is escaped 1`] = `
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
ctx[isBoundary] = 1
ctx[\`var\`] = new LazyValue(value1, ctx, this, node);
ctx[\`var\`] = new LazyValue(value1, ctx, node);
let txt1 = ctx['var'];
return block1([txt1]);
}
@@ -142,10 +134,9 @@ exports[`t-esc t-esc is escaped 1`] = `
`;
exports[`t-esc t-esc work with spread operator 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<span><block-text-0/></span>\`);
@@ -157,12 +148,11 @@ exports[`t-esc t-esc work with spread operator 1`] = `
`;
exports[`t-esc t-esc=0 is escaped 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { isBoundary, zero } = helpers;
const callTemplate_1 = app.getTemplate(\`sub\`);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, zero, getTemplate } = helpers;
const callTemplate_1 = getTemplate(\`sub\`);
let block1 = createBlock(\`<div><block-child-0/></div>\`);
let block2 = createBlock(\`<p>escaped</p>\`);
@@ -170,19 +160,18 @@ exports[`t-esc t-esc=0 is escaped 1`] = `
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
ctx[isBoundary] = 1;
const b2 = block2();
let b2 = block2();
ctx[zero] = b2;
const b3 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
let b3 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
return block1([], [b3]);
}
}"
`;
exports[`t-esc t-esc=0 is escaped 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { zero } = helpers;
let block1 = createBlock(\`<span><block-text-0/></span>\`);
@@ -195,10 +184,9 @@ exports[`t-esc t-esc=0 is escaped 2`] = `
`;
exports[`t-esc variable 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<span><block-text-0/></span>\`);
@@ -1,33 +1,31 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`t-foreach does not pollute the rendering context 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList([1]);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList([1]);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`item\`] = v_block2[i1];
const key1 = ctx['item'];
let key1 = ctx['item'];
c_block2[i1] = withKey(text(ctx['item']), key1);
}
const b2 = list(c_block2);
let b2 = list(c_block2);
return block1([], [b2]);
}
}"
`;
exports[`t-foreach iterate on items (on a element node) 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
@@ -35,101 +33,98 @@ exports[`t-foreach iterate on items (on a element node) 1`] = `
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList([1,2]);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList([1,2]);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`item\`] = v_block2[i1];
const key1 = ctx['item'];
let key1 = ctx['item'];
let txt1 = ctx['item'];
c_block2[i1] = withKey(block3([txt1]), key1);
}
const b2 = list(c_block2);
let b2 = list(c_block2);
return block1([], [b2]);
}
}"
`;
exports[`t-foreach iterate on items 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList([3,2,1]);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList([3,2,1]);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`item\`] = v_block2[i1];
ctx[\`item_index\`] = i1;
ctx[\`item_value\`] = k_block2[i1];
const key1 = ctx['item'];
const b4 = text(\` [\`);
const b5 = text(ctx['item_index']);
const b6 = text(\`: \`);
const b7 = text(ctx['item']);
const b8 = text(\` \`);
const b9 = text(ctx['item_value']);
const b10 = text(\`] \`);
let key1 = ctx['item'];
let b4 = text(\` [\`);
let b5 = text(ctx['item_index']);
let b6 = text(\`: \`);
let b7 = text(ctx['item']);
let b8 = text(\` \`);
let b9 = text(ctx['item_value']);
let b10 = text(\`] \`);
c_block2[i1] = withKey(multi([b4, b5, b6, b7, b8, b9, b10]), key1);
}
const b2 = list(c_block2);
let b2 = list(c_block2);
return block1([], [b2]);
}
}"
`;
exports[`t-foreach iterate, dict param 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['value']);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['value']);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`item\`] = v_block2[i1];
ctx[\`item_index\`] = i1;
ctx[\`item_value\`] = k_block2[i1];
const key1 = ctx['item_index'];
const b4 = text(\` [\`);
const b5 = text(ctx['item_index']);
const b6 = text(\`: \`);
const b7 = text(ctx['item']);
const b8 = text(\` \`);
const b9 = text(ctx['item_value']);
const b10 = text(\`] \`);
let key1 = ctx['item_index'];
let b4 = text(\` [\`);
let b5 = text(ctx['item_index']);
let b6 = text(\`: \`);
let b7 = text(ctx['item']);
let b8 = text(\` \`);
let b9 = text(ctx['item_value']);
let b10 = text(\`] \`);
c_block2[i1] = withKey(multi([b4, b5, b6, b7, b8, b9, b10]), key1);
}
const b2 = list(c_block2);
let b2 = list(c_block2);
return block1([], [b2]);
}
}"
`;
exports[`t-foreach iterate, position 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(Array(5));;
const [k_block2, v_block2, l_block2, c_block2] = prepareList(Array(5));
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`elem\`] = v_block2[i1];
ctx[\`elem_first\`] = i1 === 0;
ctx[\`elem_last\`] = i1 === v_block2.length - 1;
ctx[\`elem_index\`] = i1;
const key1 = ctx['elem'];
let key1 = ctx['elem'];
let b4,b5,b6,b7,b8,b9;
b4 = text(\` -\`);
if (ctx['elem_first']) {
@@ -143,48 +138,46 @@ exports[`t-foreach iterate, position 1`] = `
b9 = text(\`) \`);
c_block2[i1] = withKey(multi([b4, b5, b6, b7, b8, b9]), key1);
}
const b2 = list(c_block2);
let b2 = list(c_block2);
return block1([], [b2]);
}
}"
`;
exports[`t-foreach simple iteration (in a node) 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList([3,2,1]);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList([3,2,1]);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`item\`] = v_block2[i1];
const key1 = ctx['item'];
let key1 = ctx['item'];
c_block2[i1] = withKey(text(ctx['item']), key1);
}
const b2 = list(c_block2);
let b2 = list(c_block2);
return block1([], [b2]);
}
}"
`;
exports[`t-foreach simple iteration 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block1, v_block1, l_block1, c_block1] = prepareList([3,2,1]);;
const [k_block1, v_block1, l_block1, c_block1] = prepareList([3,2,1]);
for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`item\`] = v_block1[i1];
const key1 = ctx['item'];
let key1 = ctx['item'];
c_block1[i1] = withKey(text(ctx['item']), key1);
}
return list(c_block1);
@@ -193,10 +186,9 @@ exports[`t-foreach simple iteration 1`] = `
`;
exports[`t-foreach simple iteration with two nodes inside 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
let block3 = createBlock(\`<span>a<block-text-0/></span>\`);
@@ -204,14 +196,14 @@ exports[`t-foreach simple iteration with two nodes inside 1`] = `
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block1, v_block1, l_block1, c_block1] = prepareList([3,2,1]);;
const [k_block1, v_block1, l_block1, c_block1] = prepareList([3,2,1]);
for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`item\`] = v_block1[i1];
const key1 = ctx['item'];
let key1 = ctx['item'];
let txt1 = ctx['item'];
const b3 = block3([txt1]);
let b3 = block3([txt1]);
let txt2 = ctx['item'];
const b4 = block4([txt2]);
let b4 = block4([txt2]);
c_block1[i1] = withKey(multi([b3, b4]), key1);
}
return list(c_block1);
@@ -220,12 +212,11 @@ exports[`t-foreach simple iteration with two nodes inside 1`] = `
`;
exports[`t-foreach t-call with body in t-foreach in t-foreach 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { prepareList, isBoundary, withDefault, setContextValue, withKey } = helpers;
const callTemplate_1 = app.getTemplate(\`sub\`);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, isBoundary, withDefault, setContextValue, getTemplate, withKey } = helpers;
const callTemplate_1 = getTemplate(\`sub\`);
let block1 = createBlock(\`<div><block-child-0/><span>[<block-text-0/>][<block-text-1/>][<block-text-2/>]</span></div>\`);
let block6 = createBlock(\`<span><block-text-0/></span>\`);
@@ -234,23 +225,23 @@ exports[`t-foreach t-call with body in t-foreach in t-foreach 1`] = `
ctx = Object.create(ctx);
ctx[isBoundary] = 1
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['numbers']);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['numbers']);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`a\`] = v_block2[i1];
ctx[\`a_first\`] = i1 === 0;
ctx[\`a_last\`] = i1 === v_block2.length - 1;
ctx[\`a_index\`] = i1;
ctx[\`a_value\`] = k_block2[i1];
const key1 = ctx['a'];
let key1 = ctx['a'];
ctx = Object.create(ctx);
const [k_block4, v_block4, l_block4, c_block4] = prepareList(ctx['letters']);;
const [k_block4, v_block4, l_block4, c_block4] = prepareList(ctx['letters']);
for (let i2 = 0; i2 < l_block4; i2++) {
ctx[\`b\`] = v_block4[i2];
ctx[\`b_first\`] = i2 === 0;
ctx[\`b_last\`] = i2 === v_block4.length - 1;
ctx[\`b_index\`] = i2;
ctx[\`b_value\`] = k_block4[i2];
const key2 = ctx['b'];
let key2 = ctx['b'];
ctx = Object.create(ctx);
ctx[isBoundary] = 1;
setContextValue(ctx, \\"c\\", 'x'+'_'+ctx['a']+'_'+ctx['b']);
@@ -258,13 +249,13 @@ exports[`t-foreach t-call with body in t-foreach in t-foreach 1`] = `
ctx = ctx.__proto__;
}
ctx = ctx.__proto__;
const b4 = list(c_block4);
let b4 = list(c_block4);
let txt1 = ctx['c'];
const b6 = block6([txt1]);
let b6 = block6([txt1]);
c_block2[i1] = withKey(multi([b4, b6]), key1);
}
ctx = ctx.__proto__;
const b2 = list(c_block2);
let b2 = list(c_block2);
let txt2 = ctx['a'];
let txt3 = ctx['b'];
let txt4 = ctx['c'];
@@ -274,64 +265,62 @@ exports[`t-foreach t-call with body in t-foreach in t-foreach 1`] = `
`;
exports[`t-foreach t-call with body in t-foreach in t-foreach 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
const b2 = text(\` [\`);
const b3 = text(ctx['a']);
const b4 = text(\`] [\`);
const b5 = text(ctx['b']);
const b6 = text(\`] [\`);
const b7 = text(ctx['c']);
const b8 = text(\`] \`);
let b2 = text(\` [\`);
let b3 = text(ctx['a']);
let b4 = text(\`] [\`);
let b5 = text(ctx['b']);
let b6 = text(\`] [\`);
let b7 = text(ctx['c']);
let b8 = text(\`] \`);
return multi([b2, b3, b4, b5, b6, b7, b8]);
}
}"
`;
exports[`t-foreach t-call without body in t-foreach in t-foreach 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { prepareList, withKey } = helpers;
const callTemplate_1 = app.getTemplate(\`sub\`);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, getTemplate, withKey } = helpers;
const callTemplate_1 = getTemplate(\`sub\`);
let block1 = createBlock(\`<div><block-child-0/><span>[<block-text-0/>][<block-text-1/>][<block-text-2/>]</span></div>\`);
let block6 = createBlock(\`<span><block-text-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['numbers']);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['numbers']);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`a\`] = v_block2[i1];
ctx[\`a_first\`] = i1 === 0;
ctx[\`a_last\`] = i1 === v_block2.length - 1;
ctx[\`a_index\`] = i1;
ctx[\`a_value\`] = k_block2[i1];
const key1 = ctx['a'];
let key1 = ctx['a'];
ctx = Object.create(ctx);
const [k_block4, v_block4, l_block4, c_block4] = prepareList(ctx['letters']);;
const [k_block4, v_block4, l_block4, c_block4] = prepareList(ctx['letters']);
for (let i2 = 0; i2 < l_block4; i2++) {
ctx[\`b\`] = v_block4[i2];
ctx[\`b_first\`] = i2 === 0;
ctx[\`b_last\`] = i2 === v_block4.length - 1;
ctx[\`b_index\`] = i2;
ctx[\`b_value\`] = k_block4[i2];
const key2 = ctx['b'];
let key2 = ctx['b'];
c_block4[i2] = withKey(callTemplate_1.call(this, ctx, node, key + \`__1__\${key1}__\${key2}\`), key2);
}
ctx = ctx.__proto__;
const b4 = list(c_block4);
let b4 = list(c_block4);
let txt1 = ctx['c'];
const b6 = block6([txt1]);
let b6 = block6([txt1]);
c_block2[i1] = withKey(multi([b4, b6]), key1);
}
ctx = ctx.__proto__;
const b2 = list(c_block2);
let b2 = list(c_block2);
let txt2 = ctx['a'];
let txt3 = ctx['b'];
let txt4 = ctx['c'];
@@ -341,78 +330,75 @@ exports[`t-foreach t-call without body in t-foreach in t-foreach 1`] = `
`;
exports[`t-foreach t-call without body in t-foreach in t-foreach 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
ctx[isBoundary] = 1
setContextValue(ctx, \\"c\\", 'x'+'_'+ctx['a']+'_'+ctx['b']);
const b2 = text(\` [\`);
const b3 = text(ctx['a']);
const b4 = text(\`] [\`);
const b5 = text(ctx['b']);
const b6 = text(\`] [\`);
const b7 = text(ctx['c']);
const b8 = text(\`] \`);
let b2 = text(\` [\`);
let b3 = text(ctx['a']);
let b4 = text(\`] [\`);
let b5 = text(ctx['b']);
let b6 = text(\`] [\`);
let b7 = text(ctx['c']);
let b8 = text(\`] \`);
return multi([b2, b3, b4, b5, b6, b7, b8]);
}
}"
`;
exports[`t-foreach t-foreach in t-foreach 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['numbers']);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['numbers']);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`number\`] = v_block2[i1];
const key1 = ctx['number'];
let key1 = ctx['number'];
ctx = Object.create(ctx);
const [k_block3, v_block3, l_block3, c_block3] = prepareList(ctx['letters']);;
const [k_block3, v_block3, l_block3, c_block3] = prepareList(ctx['letters']);
for (let i2 = 0; i2 < l_block3; i2++) {
ctx[\`letter\`] = v_block3[i2];
const key2 = ctx['letter'];
const b5 = text(\` [\`);
const b6 = text(ctx['number']);
const b7 = text(ctx['letter']);
const b8 = text(\`] \`);
let key2 = ctx['letter'];
let b5 = text(\` [\`);
let b6 = text(ctx['number']);
let b7 = text(ctx['letter']);
let b8 = text(\`] \`);
c_block3[i2] = withKey(multi([b5, b6, b7, b8]), key2);
}
ctx = ctx.__proto__;
c_block2[i1] = withKey(list(c_block3), key1);
}
const b2 = list(c_block2);
let b2 = list(c_block2);
return block1([], [b2]);
}
}"
`;
exports[`t-foreach t-foreach with t-if inside (no external node) 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
let block3 = createBlock(\`<span><block-text-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block1, v_block1, l_block1, c_block1] = prepareList(ctx['elems']);;
const [k_block1, v_block1, l_block1, c_block1] = prepareList(ctx['elems']);
for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`elem\`] = v_block1[i1];
const key1 = ctx['elem'].id;
let key1 = ctx['elem'].id;
let b3;
if (ctx['elem'].id<3) {
let txt1 = ctx['elem'].text;
@@ -426,10 +412,9 @@ exports[`t-foreach t-foreach with t-if inside (no external node) 1`] = `
`;
exports[`t-foreach t-foreach with t-if inside 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
@@ -437,10 +422,10 @@ exports[`t-foreach t-foreach with t-if inside 1`] = `
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['elems']);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['elems']);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`elem\`] = v_block2[i1];
const key1 = ctx['elem'].id;
let key1 = ctx['elem'].id;
let b4;
if (ctx['elem'].id<3) {
let txt1 = ctx['elem'].text;
@@ -448,17 +433,16 @@ exports[`t-foreach t-foreach with t-if inside 1`] = `
}
c_block2[i1] = withKey(multi([b4]), key1);
}
const b2 = list(c_block2);
let b2 = list(c_block2);
return block1([], [b2]);
}
}"
`;
exports[`t-foreach t-key on t-foreach 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
@@ -466,23 +450,22 @@ exports[`t-foreach t-key on t-foreach 1`] = `
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['things']);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['things']);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`thing\`] = v_block2[i1];
const key1 = ctx['thing'];
let key1 = ctx['thing'];
c_block2[i1] = withKey(block3(), key1);
}
const b2 = list(c_block2);
let b2 = list(c_block2);
return block1([], [b2]);
}
}"
`;
exports[`t-foreach throws error if invalid loop expression 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
@@ -490,25 +473,24 @@ exports[`t-foreach throws error if invalid loop expression 1`] = `
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['abc']);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['abc']);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`item\`] = v_block2[i1];
ctx[\`item_index\`] = i1;
const key1 = ctx['item'];
let key1 = ctx['item'];
const tKey_1 = ctx['item_index'];
c_block2[i1] = withKey(block3(), tKey_1 + key1);
}
const b2 = list(c_block2);
let b2 = list(c_block2);
return block1([], [b2]);
}
}"
`;
exports[`t-foreach with t-memo 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
@@ -518,12 +500,12 @@ exports[`t-foreach with t-memo 1`] = `
let cache = ctx.cache || {};
let nextCache = ctx.cache = {};
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['items']);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['items']);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`item\`] = v_block2[i1];
const key1 = ctx['item'].id;
const memo1 = [ctx['item'].x];
const vnode1 = cache[key1];;
let key1 = ctx['item'].id;
let memo1 = [ctx['item'].x]
let vnode1 = cache[key1];
if (vnode1) {
if (shallowEqual(vnode1.memo, memo1)) {
c_block2[i1] = vnode1;
@@ -536,7 +518,7 @@ exports[`t-foreach with t-memo 1`] = `
c_block2[i1] = withKey(block3([txt1, txt2]), key1);
nextCache[key1] = Object.assign(c_block2[i1], {memo: memo1});
}
const b2 = list(c_block2);
let b2 = list(c_block2);
return block1([], [b2]);
}
}"
+62 -88
View File
@@ -1,10 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`t-if a t-if next to a div 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block2 = createBlock(\`<div>foo</div>\`);
@@ -20,10 +19,9 @@ exports[`t-if a t-if next to a div 1`] = `
`;
exports[`t-if a t-if with two inner nodes 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block3 = createBlock(\`<span>yip</span>\`);
let block4 = createBlock(\`<div>yip</div>\`);
@@ -31,8 +29,8 @@ exports[`t-if a t-if with two inner nodes 1`] = `
return function template(ctx, node, key = \\"\\") {
let b2;
if (ctx['condition']) {
const b3 = block3();
const b4 = block4();
let b3 = block3();
let b4 = block4();
b2 = multi([b3, b4]);
}
return multi([b2]);
@@ -41,10 +39,9 @@ exports[`t-if a t-if with two inner nodes 1`] = `
`;
exports[`t-if boolean value condition elif (no outside node) 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
let b2,b3,b4,b5;
@@ -63,10 +60,9 @@ exports[`t-if boolean value condition elif (no outside node) 1`] = `
`;
exports[`t-if boolean value condition elif 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/><block-child-1/><block-child-2/><block-child-3/></div>\`);
@@ -87,10 +83,9 @@ exports[`t-if boolean value condition elif 1`] = `
`;
exports[`t-if boolean value condition else 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><span>begin</span><block-child-0/><block-child-1/><span>end</span></div>\`);
@@ -107,10 +102,9 @@ exports[`t-if boolean value condition else 1`] = `
`;
exports[`t-if boolean value condition false else 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><span>begin</span><block-child-0/><block-child-1/><span>end</span></div>\`);
@@ -127,10 +121,9 @@ exports[`t-if boolean value condition false else 1`] = `
`;
exports[`t-if boolean value condition missing 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
@@ -145,10 +138,9 @@ exports[`t-if boolean value condition missing 1`] = `
`;
exports[`t-if can use some boolean operators in expressions 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/><block-child-1/><block-child-2/><block-child-3/><block-child-4/><block-child-5/><block-child-6/><block-child-7/></div>\`);
@@ -184,10 +176,9 @@ exports[`t-if can use some boolean operators in expressions 1`] = `
`;
exports[`t-if div containing a t-if with two inner nodes 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
let block3 = createBlock(\`<span>yip</span>\`);
@@ -196,8 +187,8 @@ exports[`t-if div containing a t-if with two inner nodes 1`] = `
return function template(ctx, node, key = \\"\\") {
let b2;
if (ctx['condition']) {
const b3 = block3();
const b4 = block4();
let b3 = block3();
let b4 = block4();
b2 = multi([b3, b4]);
}
return block1([], [b2]);
@@ -206,10 +197,9 @@ exports[`t-if div containing a t-if with two inner nodes 1`] = `
`;
exports[`t-if dynamic content after t-if with two children nodes 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/><block-text-0/></div>\`);
let block3 = createBlock(\`<p>1</p>\`);
@@ -218,8 +208,8 @@ exports[`t-if dynamic content after t-if with two children nodes 1`] = `
return function template(ctx, node, key = \\"\\") {
let b2;
if (ctx['condition']) {
const b3 = block3();
const b4 = block4();
let b3 = block3();
let b4 = block4();
b2 = multi([b3, b4]);
}
let txt1 = ctx['text'];
@@ -229,10 +219,9 @@ exports[`t-if dynamic content after t-if with two children nodes 1`] = `
`;
exports[`t-if just a t-if 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
let b2;
@@ -245,10 +234,9 @@ exports[`t-if just a t-if 1`] = `
`;
exports[`t-if simple t-if/t-else 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
let b2,b3;
@@ -263,10 +251,9 @@ exports[`t-if simple t-if/t-else 1`] = `
`;
exports[`t-if simple t-if/t-else in a div 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
@@ -283,10 +270,9 @@ exports[`t-if simple t-if/t-else in a div 1`] = `
`;
exports[`t-if t-esc with t-elif 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
@@ -303,10 +289,9 @@ exports[`t-if t-esc with t-elif 1`] = `
`;
exports[`t-if t-esc with t-if 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
@@ -321,10 +306,9 @@ exports[`t-if t-esc with t-if 1`] = `
`;
exports[`t-if t-if and t-else with two nodes 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block4 = createBlock(\`<span>a</span>\`);
let block5 = createBlock(\`<span>b</span>\`);
@@ -334,8 +318,8 @@ exports[`t-if t-if and t-else with two nodes 1`] = `
if (ctx['condition']) {
b2 = text(\`1\`);
} else {
const b4 = block4();
const b5 = block5();
let b4 = block4();
let b5 = block5();
b3 = multi([b4, b5]);
}
return multi([b2, b3]);
@@ -344,10 +328,9 @@ exports[`t-if t-if and t-else with two nodes 1`] = `
`;
exports[`t-if t-if in a div 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
@@ -362,10 +345,9 @@ exports[`t-if t-if in a div 1`] = `
`;
exports[`t-if t-if in a t-if 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
let block2 = createBlock(\`<span>1<block-child-0/></span>\`);
@@ -385,10 +367,9 @@ exports[`t-if t-if in a t-if 1`] = `
`;
exports[`t-if t-if with empty content 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
let b2,b3;
@@ -402,10 +383,9 @@ exports[`t-if t-if with empty content 1`] = `
`;
exports[`t-if t-if/t-else with more content 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
let b2,b3;
@@ -422,10 +402,9 @@ exports[`t-if t-if/t-else with more content 1`] = `
`;
exports[`t-if t-set, then t-if 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
@@ -444,10 +423,9 @@ exports[`t-if t-set, then t-if 1`] = `
`;
exports[`t-if t-set, then t-if, part 2 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
@@ -468,10 +446,9 @@ exports[`t-if t-set, then t-if, part 2 1`] = `
`;
exports[`t-if t-set, then t-if, part 3 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
@@ -495,10 +472,9 @@ exports[`t-if t-set, then t-if, part 3 1`] = `
`;
exports[`t-if two consecutive t-if 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
let b2,b3;
@@ -514,10 +490,9 @@ exports[`t-if two consecutive t-if 1`] = `
`;
exports[`t-if two consecutive t-if in a div 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
@@ -535,10 +510,9 @@ exports[`t-if two consecutive t-if in a div 1`] = `
`;
exports[`t-if two t-ifs next to each other 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
let block2 = createBlock(\`<span><block-text-0/></span>\`);
@@ -552,8 +526,8 @@ exports[`t-if two t-ifs next to each other 1`] = `
b2 = block2([txt1]);
}
if (ctx['condition']) {
const b4 = block4();
const b5 = block5();
let b4 = block4();
let b5 = block5();
b3 = multi([b4, b5]);
}
return block1([], [b2, b3]);
+15 -21
View File
@@ -1,10 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`t-key can use t-key directive on a node 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -17,10 +16,9 @@ exports[`t-key can use t-key directive on a node 1`] = `
`;
exports[`t-key can use t-key directive on a node 2 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -33,10 +31,9 @@ exports[`t-key can use t-key directive on a node 2 1`] = `
`;
exports[`t-key can use t-key directive on a node as a function 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -49,10 +46,9 @@ exports[`t-key can use t-key directive on a node as a function 1`] = `
`;
exports[`t-key t-key directive in a list 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
let block1 = createBlock(\`<ul><block-child-0/></ul>\`);
@@ -60,24 +56,23 @@ exports[`t-key t-key directive in a list 1`] = `
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['beers']);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['beers']);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`beer\`] = v_block2[i1];
const key1 = ctx['beer'].id;
let key1 = ctx['beer'].id;
let txt1 = ctx['beer'].name;
c_block2[i1] = withKey(block3([txt1]), key1);
}
const b2 = list(c_block2);
let b2 = list(c_block2);
return block1([], [b2]);
}
}"
`;
exports[`t-key t-key on sub dom node pushes a child block in its parent 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
let block2 = createBlock(\`<span/>\`);
@@ -96,10 +91,9 @@ exports[`t-key t-key on sub dom node pushes a child block in its parent 1`] = `
`;
exports[`t-key t-key on sub dom node pushes a child block in its parent 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><h1/></div>\`);
+96 -141
View File
@@ -1,26 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`t-out literal 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = safeOutput('ok');
let b2 = safeOutput('ok');
return block1([], [b2]);
}
}"
`;
exports[`t-out literal, no outside html element 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
return function template(ctx, node, key = \\"\\") {
@@ -30,12 +28,11 @@ exports[`t-out literal, no outside html element 1`] = `
`;
exports[`t-out multiple calls to t-out 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { isBoundary, zero } = helpers;
const callTemplate_1 = app.getTemplate(\`sub\`);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, zero, getTemplate } = helpers;
const callTemplate_1 = getTemplate(\`sub\`);
let block1 = createBlock(\`<div><block-child-0/></div>\`);
let block2 = createBlock(\`<span>coucou</span>\`);
@@ -43,70 +40,51 @@ exports[`t-out multiple calls to t-out 1`] = `
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
ctx[isBoundary] = 1;
const b2 = block2();
let b2 = block2();
ctx[zero] = b2;
const b3 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
let b3 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
return block1([], [b3]);
}
}"
`;
exports[`t-out multiple calls to t-out 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { zero } = helpers;
let block1 = createBlock(\`<div><block-child-0/><div>Greeter</div><block-child-1/></div>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = ctx[zero];
const b3 = ctx[zero];
let b2 = ctx[zero];
let b3 = ctx[zero];
return block1([], [b2, b3]);
}
}"
`;
exports[`t-out not escaping 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = safeOutput(ctx['var']);
return block1([], [b2]);
}
}"
`;
exports[`t-out number literal 1`] = `
"function anonymous(app, bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = safeOutput(1);
let b2 = safeOutput(ctx['var']);
return block1([], [b2]);
}
}"
`;
exports[`t-out t-out 0 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { isBoundary, zero } = helpers;
const callTemplate_1 = app.getTemplate(\`_basic-callee\`);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, zero, getTemplate } = helpers;
const callTemplate_1 = getTemplate(\`_basic-callee\`);
let block1 = createBlock(\`<div><block-child-0/></div>\`);
let block2 = createBlock(\`<div>zero</div>\`);
@@ -114,51 +92,48 @@ exports[`t-out t-out 0 1`] = `
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
ctx[isBoundary] = 1;
const b2 = block2();
let b2 = block2();
ctx[zero] = b2;
const b3 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
let b3 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
return block1([], [b3]);
}
}"
`;
exports[`t-out t-out 0 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { zero } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = ctx[zero];
let b2 = ctx[zero];
return block1([], [b2]);
}
}"
`;
exports[`t-out t-out and another sibling node 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<span><span>hello</span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = safeOutput(ctx['var']);
let b2 = safeOutput(ctx['var']);
return block1([], [b2]);
}
}"
`;
exports[`t-out t-out bdom 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, LazyValue, safeOutput } = helpers;
let block1 = createBlock(\`<div><span><block-child-0/></span></div>\`);
@@ -171,149 +146,140 @@ exports[`t-out t-out bdom 1`] = `
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
ctx[isBoundary] = 1
ctx[\`var\`] = new LazyValue(value1, ctx, this, node);
const b3 = safeOutput(ctx['var']);
ctx[\`var\`] = new LazyValue(value1, ctx, node);
let b3 = safeOutput(ctx['var']);
return block1([], [b3]);
}
}"
`;
exports[`t-out t-out block 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = safeOutput(ctx['bdom']);
let b2 = safeOutput(ctx['bdom']);
return block1([], [b2]);
}
}"
`;
exports[`t-out t-out escaped 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = safeOutput(ctx['var']);
let b2 = safeOutput(ctx['var']);
return block1([], [b2]);
}
}"
`;
exports[`t-out t-out markedup 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = safeOutput(ctx['var']);
let b2 = safeOutput(ctx['var']);
return block1([], [b2]);
}
}"
`;
exports[`t-out t-out on a node with a body, as a default 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput, withDefault } = helpers;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
const b3 = text(\`nope\`);
const b2 = withDefault(safeOutput(ctx['var']), b3);
let b3 = text(\`nope\`);
let b2 = withDefault(safeOutput(ctx['var']), b3);
return block1([], [b2]);
}
}"
`;
exports[`t-out t-out on a node with a dom node in body, as a default 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput, withDefault } = helpers;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
let block3 = createBlock(\`<div>nope</div>\`);
return function template(ctx, node, key = \\"\\") {
const b3 = block3();
const b2 = withDefault(safeOutput(ctx['var']), b3);
let b3 = block3();
let b2 = withDefault(safeOutput(ctx['var']), b3);
return block1([], [b2]);
}
}"
`;
exports[`t-out t-out switch escaped 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = safeOutput(ctx['var']);
let b2 = safeOutput(ctx['var']);
return block1([], [b2]);
}
}"
`;
exports[`t-out t-out switch escaped on markup 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = safeOutput(ctx['var']);
let b2 = safeOutput(ctx['var']);
return block1([], [b2]);
}
}"
`;
exports[`t-out t-out switch markup 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = safeOutput(ctx['var']);
let b2 = safeOutput(ctx['var']);
return block1([], [b2]);
}
}"
`;
exports[`t-out t-out switch markup on bdom 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, LazyValue, safeOutput } = helpers;
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
@@ -329,12 +295,12 @@ exports[`t-out t-out switch markup on bdom 1`] = `
ctx = Object.create(ctx);
ctx[isBoundary] = 1
let b3,b5;
ctx[\`bdom\`] = new LazyValue(value1, ctx, this, node);
ctx[\`bdom\`] = new LazyValue(value1, ctx, node);
if (ctx['hasBdom']) {
const b4 = safeOutput(ctx['bdom']);
let b4 = safeOutput(ctx['bdom']);
b3 = block3([], [b4]);
} else {
const b6 = safeOutput(ctx['var']);
let b6 = safeOutput(ctx['var']);
b5 = block5([], [b6]);
}
return block1([], [b3, b5]);
@@ -343,26 +309,24 @@ exports[`t-out t-out switch markup on bdom 1`] = `
`;
exports[`t-out t-out switch markup on escaped 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = safeOutput(ctx['var']);
let b2 = safeOutput(ctx['var']);
return block1([], [b2]);
}
}"
`;
exports[`t-out t-out with a <t/> in body 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
return function template(ctx, node, key = \\"\\") {
@@ -372,58 +336,54 @@ exports[`t-out t-out with a <t/> in body 1`] = `
`;
exports[`t-out t-out with arbitrary object 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = safeOutput(ctx['var']);
let b2 = safeOutput(ctx['var']);
return block1([], [b2]);
}
}"
`;
exports[`t-out t-out with arbitrary object 2 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = safeOutput(ctx['var']);
let b2 = safeOutput(ctx['var']);
return block1([], [b2]);
}
}"
`;
exports[`t-out t-out with comment 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = safeOutput(ctx['var']);
let b2 = safeOutput(ctx['var']);
return block1([], [b2]);
}
}"
`;
exports[`t-out t-out with just a t-set t-value in body 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
return function template(ctx, node, key = \\"\\") {
@@ -433,80 +393,75 @@ exports[`t-out t-out with just a t-set t-value in body 1`] = `
`;
exports[`t-out variable 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = safeOutput(ctx['var']);
let b2 = safeOutput(ctx['var']);
return block1([], [b2]);
}
}"
`;
exports[`t-out with a String class 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = safeOutput(ctx['var']);
let b2 = safeOutput(ctx['var']);
return block1([], [b2]);
}
}"
`;
exports[`t-out with an extended String class 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<span><block-child-0/></span>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = safeOutput(ctx['var']);
let b2 = safeOutput(ctx['var']);
return block1([], [b2]);
}
}"
`;
exports[`t-raw is deprecated should warn 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = safeOutput(ctx['var']);
let b2 = safeOutput(ctx['var']);
return block1([], [b2]);
}
}"
`;
exports[`t-raw is deprecated t-out is actually called in t-raw's place 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { safeOutput } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = safeOutput(ctx['var']);
let b2 = safeOutput(ctx['var']);
return block1([], [b2]);
}
}"
+21 -44
View File
@@ -1,27 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`t-ref can get a dynamic ref on a node 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let block1 = createBlock(\`<div><span block-ref=\\"0\\"/></div>\`);
return function template(ctx, node, key = \\"\\") {
const refs = ctx.__owl__.refs;
const v1 = ctx['id'];
let ref1 = (el) => refs[\`myspan\${v1}\`] = el;
return block1([ref1]);
}
}"
`;
exports[`t-ref can get a dynamic ref on a node, alternate syntax 1`] = `
"function anonymous(app, bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><span block-ref=\\"0\\"/></div>\`);
@@ -35,10 +17,9 @@ exports[`t-ref can get a dynamic ref on a node, alternate syntax 1`] = `
`;
exports[`t-ref can get a ref on a node 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><span block-ref=\\"0\\"/></div>\`);
@@ -51,26 +32,25 @@ exports[`t-ref can get a ref on a node 1`] = `
`;
exports[`t-ref ref in a t-call 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const callTemplate_1 = app.getTemplate(\`sub\`);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { getTemplate } = helpers;
const callTemplate_1 = getTemplate(\`sub\`);
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
let b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
return block1([], [b2]);
}
}"
`;
exports[`t-ref ref in a t-call 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div>1<span block-ref=\\"0\\"/>2</div>\`);
@@ -83,10 +63,9 @@ exports[`t-ref ref in a t-call 2`] = `
`;
exports[`t-ref ref in a t-if 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
let block2 = createBlock(\`<span block-ref=\\"0\\"/>\`);
@@ -104,10 +83,9 @@ exports[`t-ref ref in a t-if 1`] = `
`;
exports[`t-ref refs in a loop 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, withKey } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
@@ -116,27 +94,26 @@ exports[`t-ref refs in a loop 1`] = `
return function template(ctx, node, key = \\"\\") {
const refs = ctx.__owl__.refs;
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['items']);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['items']);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`item\`] = v_block2[i1];
const key1 = ctx['item'];
let key1 = ctx['item'];
const tKey_1 = ctx['item'];
const v1 = ctx['item'];
let ref1 = (el) => refs[(v1)] = el;
let ref1 = (el) => refs[\`\${v1}\`] = el;
let txt1 = ctx['item'];
c_block2[i1] = withKey(block3([ref1, txt1]), tKey_1 + key1);
}
const b2 = list(c_block2);
let b2 = list(c_block2);
return block1([], [b2]);
}
}"
`;
exports[`t-ref two refs, one in a t-if 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-child-0/><p block-ref=\\"0\\"/></div>\`);
let block2 = createBlock(\`<span block-ref=\\"0\\"/>\`);
+88 -117
View File
@@ -1,10 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`t-set evaluate value expression 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -20,10 +19,9 @@ exports[`t-set evaluate value expression 1`] = `
`;
exports[`t-set evaluate value expression, part 2 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -39,10 +37,9 @@ exports[`t-set evaluate value expression, part 2 1`] = `
`;
exports[`t-set set from attribute literal (no outside div) 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
return function template(ctx, node, key = \\"\\") {
@@ -55,10 +52,9 @@ exports[`t-set set from attribute literal (no outside div) 1`] = `
`;
exports[`t-set set from attribute literal 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -74,10 +70,9 @@ exports[`t-set set from attribute literal 1`] = `
`;
exports[`t-set set from attribute lookup 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -93,10 +88,9 @@ exports[`t-set set from attribute lookup 1`] = `
`;
exports[`t-set set from body literal (with t-if/t-else 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, LazyValue } = helpers;
function value1(ctx, node, key = \\"\\") {
@@ -112,17 +106,16 @@ exports[`t-set set from body literal (with t-if/t-else 1`] = `
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
ctx[isBoundary] = 1
ctx[\`value\`] = new LazyValue(value1, ctx, this, node);
ctx[\`value\`] = new LazyValue(value1, ctx, node);
return text(ctx['value']);
}
}"
`;
exports[`t-set set from body literal 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
return function template(ctx, node, key = \\"\\") {
@@ -135,10 +128,9 @@ exports[`t-set set from body literal 1`] = `
`;
exports[`t-set set from body lookup 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, LazyValue } = helpers;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -150,7 +142,7 @@ exports[`t-set set from body lookup 1`] = `
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
ctx[isBoundary] = 1
ctx[\`stuff\`] = new LazyValue(value1, ctx, this, node);
ctx[\`stuff\`] = new LazyValue(value1, ctx, node);
let txt1 = ctx['stuff'];
return block1([txt1]);
}
@@ -158,10 +150,9 @@ exports[`t-set set from body lookup 1`] = `
`;
exports[`t-set set from empty body 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -177,10 +168,9 @@ exports[`t-set set from empty body 1`] = `
`;
exports[`t-set t-set and t-if 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
@@ -199,10 +189,9 @@ exports[`t-set t-set and t-if 1`] = `
`;
exports[`t-set t-set body is evaluated immediately 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue, LazyValue, safeOutput } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
@@ -217,21 +206,20 @@ exports[`t-set t-set body is evaluated immediately 1`] = `
ctx = Object.create(ctx);
ctx[isBoundary] = 1
setContextValue(ctx, \\"v1\\", 'before');
ctx[\`v2\`] = new LazyValue(value1, ctx, this, node);
ctx[\`v2\`] = new LazyValue(value1, ctx, node);
setContextValue(ctx, \\"v1\\", 'after');
const b3 = safeOutput(ctx['v2']);
let b3 = safeOutput(ctx['v2']);
return block1([], [b3]);
}
}"
`;
exports[`t-set t-set can't alter from within callee 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
const callTemplate_1 = app.getTemplate(\`sub\`);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue, getTemplate } = helpers;
const callTemplate_1 = getTemplate(\`sub\`);
let block1 = createBlock(\`<div><p><block-text-0/></p><block-child-0/><p><block-text-1/></p></div>\`);
@@ -240,7 +228,7 @@ exports[`t-set t-set can't alter from within callee 1`] = `
ctx[isBoundary] = 1
setContextValue(ctx, \\"iter\\", 'source');
let txt1 = ctx['iter'];
const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
let b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
let txt2 = ctx['iter'];
return block1([txt1, txt2], [b2]);
}
@@ -248,10 +236,9 @@ exports[`t-set t-set can't alter from within callee 1`] = `
`;
exports[`t-set t-set can't alter from within callee 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-text-0/><block-text-1/></div>\`);
@@ -268,12 +255,11 @@ exports[`t-set t-set can't alter from within callee 2`] = `
`;
exports[`t-set t-set can't alter in t-call body 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
const callTemplate_1 = app.getTemplate(\`sub\`);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue, getTemplate } = helpers;
const callTemplate_1 = getTemplate(\`sub\`);
let block1 = createBlock(\`<div><p><block-text-0/></p><block-child-0/><p><block-text-1/></p></div>\`);
@@ -285,7 +271,7 @@ exports[`t-set t-set can't alter in t-call body 1`] = `
ctx = Object.create(ctx);
ctx[isBoundary] = 1;
setContextValue(ctx, \\"iter\\", 'inCall');
const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
let b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
ctx = ctx.__proto__;
let txt2 = ctx['iter'];
return block1([txt1, txt2], [b2]);
@@ -294,10 +280,9 @@ exports[`t-set t-set can't alter in t-call body 1`] = `
`;
exports[`t-set t-set can't alter in t-call body 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-text-0/><block-text-1/></div>\`);
@@ -314,10 +299,9 @@ exports[`t-set t-set can't alter in t-call body 2`] = `
`;
exports[`t-set t-set does not modify render context existing key values 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -333,10 +317,9 @@ exports[`t-set t-set does not modify render context existing key values 1`] = `
`;
exports[`t-set t-set evaluates an expression only once 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-text-0/><block-text-1/></div>\`);
@@ -353,10 +336,9 @@ exports[`t-set t-set evaluates an expression only once 1`] = `
`;
exports[`t-set t-set outside modified in t-foreach 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue, prepareList, withKey } = helpers;
let block1 = createBlock(\`<div><block-child-0/><p>EndLoop: <block-text-0/></p></div>\`);
@@ -367,16 +349,16 @@ exports[`t-set t-set outside modified in t-foreach 1`] = `
ctx[isBoundary] = 1
setContextValue(ctx, \\"iter\\", 0);
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(['a','b']);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList(['a','b']);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`val\`] = v_block2[i1];
const key1 = ctx['val'];
let key1 = ctx['val'];
let txt1 = ctx['iter'];
c_block2[i1] = withKey(block3([txt1]), key1);
setContextValue(ctx, \\"iter\\", ctx['iter']+1);
}
ctx = ctx.__proto__;
const b2 = list(c_block2);
let b2 = list(c_block2);
let txt2 = ctx['iter'];
return block1([txt2], [b2]);
}
@@ -384,10 +366,9 @@ exports[`t-set t-set outside modified in t-foreach 1`] = `
`;
exports[`t-set t-set outside modified in t-foreach increment-after operator 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue, prepareList, withKey } = helpers;
let block1 = createBlock(\`<div><block-child-0/><p>EndLoop: <block-text-0/></p></div>\`);
@@ -398,16 +379,16 @@ exports[`t-set t-set outside modified in t-foreach increment-after operator 1`]
ctx[isBoundary] = 1
setContextValue(ctx, \\"iter\\", 0);
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(['a','b']);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList(['a','b']);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`val\`] = v_block2[i1];
const key1 = ctx['val'];
let key1 = ctx['val'];
let txt1 = ctx['iter'];
c_block2[i1] = withKey(block3([txt1]), key1);
setContextValue(ctx, \\"iter\\", ctx['iter']++);
}
ctx = ctx.__proto__;
const b2 = list(c_block2);
let b2 = list(c_block2);
let txt2 = ctx['iter'];
return block1([txt2], [b2]);
}
@@ -415,10 +396,9 @@ exports[`t-set t-set outside modified in t-foreach increment-after operator 1`]
`;
exports[`t-set t-set outside modified in t-foreach increment-before operator 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue, prepareList, withKey } = helpers;
let block1 = createBlock(\`<div><block-child-0/><p>EndLoop: <block-text-0/></p></div>\`);
@@ -429,16 +409,16 @@ exports[`t-set t-set outside modified in t-foreach increment-before operator 1`]
ctx[isBoundary] = 1
setContextValue(ctx, \\"iter\\", 0);
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(['a','b']);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList(['a','b']);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`val\`] = v_block2[i1];
const key1 = ctx['val'];
let key1 = ctx['val'];
let txt1 = ctx['iter'];
c_block2[i1] = withKey(block3([txt1]), key1);
setContextValue(ctx, \\"iter\\", ++ctx['iter']);
}
ctx = ctx.__proto__;
const b2 = list(c_block2);
let b2 = list(c_block2);
let txt2 = ctx['iter'];
return block1([txt2], [b2]);
}
@@ -446,10 +426,9 @@ exports[`t-set t-set outside modified in t-foreach increment-before operator 1`]
`;
exports[`t-set t-set should reuse variable if possible 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue, prepareList, withKey } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
@@ -460,40 +439,39 @@ exports[`t-set t-set should reuse variable if possible 1`] = `
ctx[isBoundary] = 1
setContextValue(ctx, \\"v\\", 1);
ctx = Object.create(ctx);
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['list']);;
const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['list']);
for (let i1 = 0; i1 < l_block2; i1++) {
ctx[\`elem\`] = v_block2[i1];
ctx[\`elem_index\`] = i1;
const key1 = ctx['elem_index'];
let key1 = ctx['elem_index'];
let txt1 = ctx['v'];
setContextValue(ctx, \\"v\\", ctx['elem']);
c_block2[i1] = withKey(block3([txt1]), key1);
}
const b2 = list(c_block2);
let b2 = list(c_block2);
return block1([], [b2]);
}
}"
`;
exports[`t-set t-set with content and sub t-esc 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, LazyValue } = helpers;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
function value1(ctx, node, key = \\"\\") {
const b3 = text(ctx['beep']);
const b4 = text(\` boop\`);
let b3 = text(ctx['beep']);
let b4 = text(\` boop\`);
return multi([b3, b4]);
}
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
ctx[isBoundary] = 1
ctx[\`setvar\`] = new LazyValue(value1, ctx, this, node);
ctx[\`setvar\`] = new LazyValue(value1, ctx, node);
let txt1 = ctx['setvar'];
return block1([txt1]);
}
@@ -501,10 +479,9 @@ exports[`t-set t-set with content and sub t-esc 1`] = `
`;
exports[`t-set t-set with t-value (falsy) and body 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue, LazyValue, safeOutput } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
@@ -520,20 +497,19 @@ exports[`t-set t-set with t-value (falsy) and body 1`] = `
ctx[isBoundary] = 1
setContextValue(ctx, \\"v3\\", false);
setContextValue(ctx, \\"v1\\", 'before');
ctx[\`v2\`] = withDefault(ctx['v3'], new LazyValue(value1, ctx, this, node));
ctx[\`v2\`] = withDefault(ctx['v3'], new LazyValue(value1, ctx, node));
setContextValue(ctx, \\"v1\\", 'after');
setContextValue(ctx, \\"v3\\", true);
const b3 = safeOutput(ctx['v2']);
let b3 = safeOutput(ctx['v2']);
return block1([], [b3]);
}
}"
`;
exports[`t-set t-set with t-value (truthy) and body 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue, LazyValue, safeOutput } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
@@ -549,20 +525,19 @@ exports[`t-set t-set with t-value (truthy) and body 1`] = `
ctx[isBoundary] = 1
setContextValue(ctx, \\"v3\\", 'Truthy');
setContextValue(ctx, \\"v1\\", 'before');
ctx[\`v2\`] = withDefault(ctx['v3'], new LazyValue(value1, ctx, this, node));
ctx[\`v2\`] = withDefault(ctx['v3'], new LazyValue(value1, ctx, node));
setContextValue(ctx, \\"v1\\", 'after');
setContextValue(ctx, \\"v3\\", false);
const b3 = safeOutput(ctx['v2']);
let b3 = safeOutput(ctx['v2']);
return block1([], [b3]);
}
}"
`;
exports[`t-set t-set, t-if, and mix of expression/body lookup, 1 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/><block-child-0/><block-text-0/></div>\`);
@@ -582,10 +557,9 @@ exports[`t-set t-set, t-if, and mix of expression/body lookup, 1 1`] = `
`;
exports[`t-set t-set, t-if, and mix of expression/body lookup, 2 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/><block-child-0/><block-text-0/></div>\`);
@@ -605,10 +579,9 @@ exports[`t-set t-set, t-if, and mix of expression/body lookup, 2 1`] = `
`;
exports[`t-set t-set, t-if, and mix of expression/body lookup, 3 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
return function template(ctx, node, key = \\"\\") {
@@ -627,10 +600,9 @@ exports[`t-set t-set, t-if, and mix of expression/body lookup, 3 1`] = `
`;
exports[`t-set value priority (with non text body 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, LazyValue } = helpers;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -643,7 +615,7 @@ exports[`t-set value priority (with non text body 1`] = `
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
ctx[isBoundary] = 1
ctx[\`value\`] = withDefault(1, new LazyValue(value1, ctx, this, node));
ctx[\`value\`] = withDefault(1, new LazyValue(value1, ctx, node));
let txt1 = ctx['value'];
return block1([txt1]);
}
@@ -651,10 +623,9 @@ exports[`t-set value priority (with non text body 1`] = `
`;
exports[`t-set value priority 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { isBoundary, withDefault, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
+29 -37
View File
@@ -1,127 +1,119 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`qweb t-tag can fallback if falsy tag 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = tag => createBlock(\`<\${tag || 'fallback'}/>\`);
return function template(ctx, node, key = \\"\\") {
const tag1 = ctx['tag'];
let tag1 = ctx['tag'];
return toggler(tag1, block1(tag1)());
}
}"
`;
exports[`qweb t-tag can update 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = tag => createBlock(\`<\${tag || 't'}/>\`);
return function template(ctx, node, key = \\"\\") {
const tag1 = ctx['tag'];
let tag1 = ctx['tag'];
return toggler(tag1, block1(tag1)());
}
}"
`;
exports[`qweb t-tag simple usecases 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = tag => createBlock(\`<\${tag || 't'}/>\`);
return function template(ctx, node, key = \\"\\") {
const tag1 = 'div';
let tag1 = 'div';
return toggler(tag1, block1(tag1)());
}
}"
`;
exports[`qweb t-tag simple usecases 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = tag => createBlock(\`<\${tag || 't'}>text</\${tag || 't'}>\`);
return function template(ctx, node, key = \\"\\") {
const tag1 = ctx['tag'];
let tag1 = ctx['tag'];
return toggler(tag1, block1(tag1)());
}
}"
`;
exports[`qweb t-tag with multiple attributes 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = tag => createBlock(\`<\${tag || 't'} class=\\"blueberry\\" taste=\\"raspberry\\">gooseberry</\${tag || 't'}>\`);
return function template(ctx, node, key = \\"\\") {
const tag1 = ctx['tag'];
let tag1 = ctx['tag'];
return toggler(tag1, block1(tag1)());
}
}"
`;
exports[`qweb t-tag with multiple child nodes 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = tag => createBlock(\`<\${tag || 't'}> pear <span>apple</span> strawberry </\${tag || 't'}>\`);
return function template(ctx, node, key = \\"\\") {
const tag1 = ctx['tag'];
let tag1 = ctx['tag'];
return toggler(tag1, block1(tag1)());
}
}"
`;
exports[`qweb t-tag with multiple t-tag in same template 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = tag => createBlock(\`<\${tag || 't'}><block-child-0/></\${tag || 't'}>\`);
let block2 = tag => createBlock(\`<\${tag || 't'}>baz</\${tag || 't'}>\`);
return function template(ctx, node, key = \\"\\") {
const tag1 = ctx['outer'];
const tag2 = ctx['inner'];
const b2 = toggler(tag2, block2(tag2)());
let tag1 = ctx['outer'];
let tag2 = ctx['inner'];
let b2 = toggler(tag2, block2(tag2)());
return toggler(tag1, block1(tag1)([], [b2]));
}
}"
`;
exports[`qweb t-tag with multiple t-tag in same template, part 2 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block2 = tag => createBlock(\`<\${tag || 't'}>bar</\${tag || 't'}>\`);
let block3 = tag => createBlock(\`<\${tag || 't'}>baz</\${tag || 't'}>\`);
return function template(ctx, node, key = \\"\\") {
const tag1 = ctx['brother'];
const b2 = toggler(tag1, block2(tag1)());
const tag2 = ctx['brother'];
const b3 = toggler(tag2, block3(tag2)());
let tag1 = ctx['brother'];
let b2 = toggler(tag1, block2(tag1)());
let tag2 = ctx['brother'];
let b3 = toggler(tag2, block3(tag2)());
return multi([b2, b3]);
}
}"
@@ -1,10 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`loading templates addTemplates does not modify its xml document in place 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><block-text-0/></div>\`);
@@ -16,10 +15,9 @@ exports[`loading templates addTemplates does not modify its xml document in plac
`;
exports[`loading templates can initialize qweb with a string 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div>jupiler</div>\`);
@@ -30,10 +28,9 @@ exports[`loading templates can initialize qweb with a string 1`] = `
`;
exports[`loading templates can initialize qweb with an XMLDocument 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div>jupiler</div>\`);
@@ -44,66 +41,64 @@ exports[`loading templates can initialize qweb with an XMLDocument 1`] = `
`;
exports[`loading templates can load a few templates from a xml string 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const callTemplate_1 = app.getTemplate(\`items\`);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { getTemplate } = helpers;
const callTemplate_1 = getTemplate(\`items\`);
let block1 = createBlock(\`<ul><block-child-0/></ul>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
let b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
return block1([], [b2]);
}
}"
`;
exports[`loading templates can load a few templates from a xml string 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block2 = createBlock(\`<li>ok</li>\`);
let block3 = createBlock(\`<li>foo</li>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = block2();
const b3 = block3();
let b2 = block2();
let b3 = block3();
return multi([b2, b3]);
}
}"
`;
exports[`loading templates can load a few templates from an XMLDocument 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
const callTemplate_1 = app.getTemplate(\`items\`);
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { getTemplate } = helpers;
const callTemplate_1 = getTemplate(\`items\`);
let block1 = createBlock(\`<ul><block-child-0/></ul>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
let b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`);
return block1([], [b2]);
}
}"
`;
exports[`loading templates can load a few templates from an XMLDocument 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block2 = createBlock(\`<li>ok</li>\`);
let block3 = createBlock(\`<li>foo</li>\`);
return function template(ctx, node, key = \\"\\") {
const b2 = block2();
const b3 = block3();
let b2 = block2();
let b3 = block3();
return multi([b2, b3]);
}
}"
@@ -1,10 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`translation support can set and remove translatable attributes 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div tomato=\\"word\\" potato=\\"mot\\" title=\\"mot\\" label=\\"word\\">text</div>\`);
@@ -15,10 +14,9 @@ exports[`translation support can set and remove translatable attributes 1`] = `
`;
exports[`translation support can translate node content 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div>mot</div>\`);
@@ -29,10 +27,9 @@ exports[`translation support can translate node content 1`] = `
`;
exports[`translation support does not translate node content if disabled 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><span>mot</span><span>word</span></div>\`);
@@ -43,10 +40,9 @@ exports[`translation support does not translate node content if disabled 1`] = `
`;
exports[`translation support some attributes are translated 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><p label=\\"mot\\">mot</p><p title=\\"mot\\">mot</p><p placeholder=\\"mot\\">mot</p><p alt=\\"mot\\">mot</p><p something=\\"word\\">mot</p></div>\`);
@@ -57,10 +53,9 @@ exports[`translation support some attributes are translated 1`] = `
`;
exports[`translation support translation is done on the trimmed text, with extra spaces readded after 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div> mot </div>\`);
@@ -1,10 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`white space handling consecutives whitespaces are condensed into a single space 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div> abc </div>\`);
@@ -15,10 +14,9 @@ exports[`white space handling consecutives whitespaces are condensed into a sing
`;
exports[`white space handling nothing is done in pre tags 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<pre> </pre>\`);
@@ -29,10 +27,9 @@ exports[`white space handling nothing is done in pre tags 1`] = `
`;
exports[`white space handling nothing is done in pre tags 2`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<pre>
some text
@@ -45,10 +42,9 @@ exports[`white space handling nothing is done in pre tags 2`] = `
`;
exports[`white space handling nothing is done in pre tags 3`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<pre>
@@ -61,10 +57,9 @@ exports[`white space handling nothing is done in pre tags 3`] = `
`;
exports[`white space handling pre inside a div with a new line 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><pre>SomeText</pre></div>\`);
@@ -75,10 +70,9 @@ exports[`white space handling pre inside a div with a new line 1`] = `
`;
exports[`white space handling white space only text nodes are condensed into a single space 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div> </div>\`);
@@ -89,10 +83,9 @@ exports[`white space handling white space only text nodes are condensed into a s
`;
exports[`white space handling whitespace only text nodes with newlines are removed 1`] = `
"function anonymous(app, bdom, helpers
"function anonymous(bdom, helpers
) {
\\"use strict\\";
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div><span>abc</span></div>\`);
+1 -49
View File
@@ -1,4 +1,4 @@
import { mount, patch } from "../../src/runtime/blockdom";
import { mount, patch } from "../../src/blockdom";
import { makeTestFixture, renderToBdom, renderToString, snapshotEverything } from "../helpers";
snapshotEverything();
@@ -22,12 +22,6 @@ describe("attributes", () => {
expect(renderToString(template)).toBe(`<div aria-label="Close"></div>`);
});
test("static attributes with backticks", () => {
const template = '<div foo="`bar`"></div>';
const result = renderToString(template);
expect(result).toBe('<div foo="`bar`"></div>');
});
test("static attributes on void elements", () => {
const template = `<img src="/test.skip.jpg" alt="Test"/>`;
expect(renderToString(template)).toBe(`<img src="/test.skip.jpg" alt="Test">`);
@@ -39,12 +33,6 @@ describe("attributes", () => {
expect(result).toBe(`<div foo="bar"></div>`);
});
test("dynamic attributes with backticks", () => {
const template = '<div t-att-foo="`bar`"/>';
const result = renderToString(template);
expect(result).toBe(`<div foo="bar"></div>`);
});
test("two dynamic attributes", () => {
const template = `<div t-att-foo="'bar'" t-att-bar="'foo'"/>`;
const result = renderToString(template);
@@ -63,24 +51,6 @@ describe("attributes", () => {
expect(result).toBe(`<div></div>`);
});
test("dynamic class attribute which is only a space", () => {
const template = `<div t-att-class="c"/>`;
const result = renderToString(template, { c: " " });
expect(result).toBe(`<div></div>`);
});
test("dynamic class attribute with multiple consecutive spaces", () => {
const template = `<div t-att-class="c"/>`;
const result = renderToString(template, { c: "a b" });
expect(result).toBe(`<div class="a b"></div>`);
});
test("dynamic class attribute that starts and ends with a space", () => {
const template = `<div t-att-class="c"/>`;
const result = renderToString(template, { c: " a " });
expect(result).toBe(`<div class="a"></div>`);
});
test("dynamic undefined generic attribute", () => {
const template = `<div t-att-thing="c"/>`;
const result = renderToString(template, { c: undefined });
@@ -161,12 +131,6 @@ describe("attributes", () => {
expect(result).toBe(`<div foo="bar"></div>`);
});
test("string interpolation, alternate syntax", () => {
const template = `<div t-attf-foo="b#{value}r"/>`;
const result = renderToString(template, { value: "a" });
expect(result).toBe(`<div foo="bar"></div>`);
});
test("t-attf-class", () => {
const template = `<div t-attf-class="hello"/>`;
const result = renderToString(template);
@@ -278,14 +242,6 @@ describe("attributes", () => {
const template = `<div class="static" t-att-class="{a: b, c: d, e: f}"/>`;
const result = renderToString(template, { b: true, d: false, f: true });
expect(result).toBe(`<div class="static a e"></div>`);
// leading and trailing space in the key
expect(renderToString(`<div t-att-class="{' a ': value}" />`, { value: true })).toBe(
'<div class="a"></div>'
);
// whitespace only key
expect(renderToString(`<div t-att-class="{' ': value}" />`, { value: true })).toBe(
"<div></div>"
);
});
test("t-att-class with multiple classes", () => {
@@ -295,10 +251,6 @@ describe("attributes", () => {
expect(renderToString(`<div t-att-class="{['a b c']: value}" />`, { value: true })).toBe(
'<div class="a b c"></div>'
);
// multiple spaces between classes
expect(renderToString(`<div t-att-class="{'a b c': value}" />`, { value: true })).toBe(
'<div class="a b c"></div>'
);
});
test("t-att-class with multiple classes, some of which are duplicate", () => {
-13
View File
@@ -1,13 +0,0 @@
import { renderToString } from "../helpers";
describe("blacklisted tags and attributes", () => {
test("template with block-text tag", () => {
const template = `<div><block-text-0/>hello</div>`;
expect(() => renderToString(template)).toThrow("Invalid tag name: 'block-text-0'");
});
test("template with block-handler tag", () => {
const template = `<div block-handler-0="click">hello</div>`;
expect(() => renderToString(template)).toThrow("Invalid attribute: 'block-handler-0'");
});
});
+9
View File
@@ -12,6 +12,15 @@ describe("error handling", () => {
expect(() => context.renderToString("invalidname")).toThrow("Missing template");
});
test("cannot add twice the same template", () => {
const context = new TestContext();
context.addTemplate("test", `<t></t>`);
expect(() => context.addTemplate("test", "<div/>", { allowDuplicate: true })).not.toThrow(
"already defined"
);
expect(() => context.addTemplate("test", "<div/>")).toThrow("already defined");
});
test("addTemplates throw if parser error", () => {
const context = new TestContext();
expect(() => {
+4 -4
View File
@@ -1,8 +1,8 @@
import { TemplateSet } from "../../src/runtime/template_set";
import { mount } from "../../src/runtime/blockdom";
import { TemplateSet } from "../../src/app/template_set";
import { mount } from "../../src/blockdom";
import { makeTestFixture, renderToBdom, renderToString, snapshotEverything } from "../helpers";
import { markup } from "../../src/runtime/utils";
import { STATUS } from "../../src/runtime/status";
import { markup } from "../../src/utils";
import { STATUS } from "../../src/component/status";
snapshotEverything();
// -----------------------------------------------------------------------------
+6 -19
View File
@@ -65,14 +65,14 @@ describe("tokenizer", () => {
});
test("strings", () => {
expect(() => tokenize("'")).toThrow("Tokenizer error: could not tokenize `'`");
expect(() => tokenize("'\\")).toThrow("Tokenizer error: could not tokenize `'\\`");
expect(() => tokenize("'\\'")).toThrow("Tokenizer error: could not tokenize `'\\'`");
expect(() => tokenize("'")).toThrow("Invalid expression");
expect(() => tokenize("'\\")).toThrow("Invalid expression");
expect(() => tokenize("'\\'")).toThrow("Invalid expression");
expect(tokenize("'hello ged'")).toEqual([{ type: "VALUE", value: "'hello ged'" }]);
expect(tokenize("'hello \\'ged\\''")).toEqual([{ type: "VALUE", value: "'hello \\'ged\\''" }]);
expect(() => tokenize('"')).toThrow('Tokenizer error: could not tokenize `"`');
expect(() => tokenize('"\\"')).toThrow('Tokenizer error: could not tokenize `"\\"`');
expect(() => tokenize('"')).toThrow("Invalid expression");
expect(() => tokenize('"\\"')).toThrow("Invalid expression");
expect(tokenize('"hello ged"')).toEqual([{ type: "VALUE", value: '"hello ged"' }]);
expect(tokenize('"hello ged"}')).toEqual([
{ type: "VALUE", value: '"hello ged"' },
@@ -135,7 +135,7 @@ describe("expression evaluation", () => {
expect(compileExpr("color === 'black'")).toBe("ctx['color']==='black'");
expect(compileExpr("'li_'+item")).toBe("'li_'+ctx['item']");
expect(compileExpr("state.val > 1")).toBe("ctx['state'].val>1");
expect(compileExpr("a in b")).toBe("ctx['a'] in ctx['b']");
expect(compileExpr("a in b")).toBe("ctx['a']in ctx['b']");
});
test("boolean operations", () => {
@@ -215,17 +215,4 @@ describe("expression evaluation", () => {
expect(compileExpr("[a, {b, c},d]")).toBe("[ctx['a'],{b:ctx['b'],c:ctx['c']},ctx['d']]");
expect(compileExpr("{a:[b, {c, d: e}]}")).toBe("{a:[ctx['b'],{c:ctx['c'],d:ctx['e']}]}");
});
test("preserving spaces where needed for text operators", () => {
expect(compileExpr("new Date()")).toBe("new Date()");
expect(compileExpr("a.c in b")).toBe("ctx['a'].c in ctx['b']");
expect(compileExpr("typeof val")).toBe("typeof ctx['val']");
});
test("binary operators", () => {
expect(compileExpr("1 | 1")).toBe("1|1");
expect(compileExpr("1 & 1")).toBe("1&1");
expect(compileExpr("1 ^ 1")).toBe("1^1");
expect(compileExpr("~1")).toBe("~1");
});
});

Some files were not shown because too many files have changed in this diff Show More