From 1364d557de5aa6c1546fd46f6b414aced29627ba Mon Sep 17 00:00:00 2001 From: Dale Bustad Date: Tue, 6 Feb 2024 01:59:28 -0800 Subject: [PATCH 01/13] chore: update fixtures to accommodate SSR compiler --- .../src/__tests__/fixtures/attribute-aria-modify/index.js | 1 + .../src/__tests__/fixtures/attribute-aria/index.js | 1 + .../src/__tests__/fixtures/attribute-boolean/index.js | 3 ++- .../src/__tests__/fixtures/attribute-component-aria/index.js | 3 ++- .../fixtures/attribute-component-global-html/index.js | 3 ++- .../src/__tests__/fixtures/attribute-dynamic-escape/index.js | 3 ++- .../fixtures/attribute-dynamic-with-scoped-css/index.js | 3 ++- .../src/__tests__/fixtures/attribute-dynamic/index.js | 3 ++- .../src/__tests__/fixtures/attribute-global-html/index.js | 3 ++- .../src/__tests__/fixtures/attribute-live-bindings/index.js | 3 ++- .../src/__tests__/fixtures/attribute-static/index.js | 3 ++- .../src/__tests__/fixtures/attribute-style/index.js | 3 ++- .../src/__tests__/fixtures/attributes-aria/index.js | 3 ++- .../src/__tests__/fixtures/comments-basic/index.js | 3 ++- .../src/__tests__/fixtures/comments-foreach/index.js | 3 ++- .../engine-server/src/__tests__/fixtures/comments-if/index.js | 3 ++- .../src/__tests__/fixtures/comments-slot/index.js | 3 ++- .../engine-server/src/__tests__/fixtures/component/index.js | 3 ++- .../engine-server/src/__tests__/fixtures/computed/index.js | 3 ++- .../engine-server/src/__tests__/fixtures/context-deep/index.js | 1 + .../fixtures/context-installation-on-non-element/index.js | 1 + .../src/__tests__/fixtures/context-nested/index.js | 1 + .../src/__tests__/fixtures/context-no-provider/index.js | 1 + .../src/__tests__/fixtures/context-shadowed/index.js | 1 + .../src/__tests__/fixtures/context-simple/index.js | 1 + .../src/__tests__/fixtures/context-slotted/index.js | 1 + .../__tests__/fixtures/custom-styles-with-newlines/index.js | 3 ++- .../src/__tests__/fixtures/delegates-focus/index.js | 3 ++- .../src/__tests__/fixtures/directive-external/index.js | 1 + .../src/__tests__/fixtures/dynamic-component-no-ctor/index.js | 3 ++- .../src/__tests__/fixtures/dynamic-components/index.js | 3 ++- .../src/__tests__/fixtures/dynamic-slots/index.js | 3 ++- .../src/__tests__/fixtures/face-callback/index.js | 3 ++- .../src/__tests__/fixtures/for-each-block/index.js | 3 ++- .../src/__tests__/fixtures/for-each-child-nested/index.js | 3 ++- .../src/__tests__/fixtures/for-each-nested/index.js | 3 ++- .../src/__tests__/fixtures/for-static-content/index.js | 1 + .../src/__tests__/fixtures/getter-class-list/index.js | 3 ++- .../src/__tests__/fixtures/getter-is-connected/index.js | 3 ++- .../engine-server/src/__tests__/fixtures/if-block/index.js | 3 ++- .../__tests__/fixtures/if-conditional-slot-content/index.js | 3 ++- .../src/__tests__/fixtures/if-conditional-slot/index.js | 3 ++- .../src/__tests__/fixtures/if-custom-element-child/index.js | 3 ++- .../src/__tests__/fixtures/if-element-child/index.js | 3 ++- .../src/__tests__/fixtures/iterator-block/index.js | 3 ++- .../src/__tests__/fixtures/lifecycle-hooks/index.js | 3 ++- .../src/__tests__/fixtures/light-dom-multiple/index.js | 1 + .../src/__tests__/fixtures/light-dom-scoped-styles/index.js | 1 + .../engine-server/src/__tests__/fixtures/light-dom/index.js | 3 ++- .../src/__tests__/fixtures/lwc-dom-manual/index.js | 3 ++- .../engine-server/src/__tests__/fixtures/lwc-dynamic/index.js | 3 ++- .../src/__tests__/fixtures/lwc-inner-html/index.js | 3 ++- .../src/__tests__/fixtures/method-get-attribute/index.js | 3 ++- .../src/__tests__/fixtures/method-remove-attribute/index.js | 3 ++- .../src/__tests__/fixtures/method-set-attribute/index.js | 3 ++- .../engine-server/src/__tests__/fixtures/methods-noop/index.js | 3 ++- .../src/__tests__/fixtures/methods-unsupported/index.js | 3 ++- .../__tests__/fixtures/no-boundary-protection-basic/index.js | 1 + .../__tests__/fixtures/no-boundary-protection-child/index.js | 1 + .../fixtures/no-boundary-protection-deep-child/index.js | 1 + .../__tests__/fixtures/no-boundary-protection-slotted/index.js | 1 + .../__tests__/fixtures/overwrite-properties-template/index.js | 3 ++- .../src/__tests__/fixtures/overwrite-properties/index.js | 3 ++- .../fixtures/parent-child-connected-callback/index.js | 1 + .../__tests__/fixtures/parent-child-read-only-prop/index.js | 1 + .../engine-server/src/__tests__/fixtures/rehydration/index.js | 3 ++- .../src/__tests__/fixtures/scoped-slots/basic/index.js | 3 ++- .../src/__tests__/fixtures/scoped-slots/for-each/index.js | 3 ++- .../src/__tests__/fixtures/scoped-slots/named-slot/index.js | 3 ++- .../engine-server/src/__tests__/fixtures/slotchange/index.js | 3 ++- .../src/__tests__/fixtures/slots-fallback/index.js | 3 ++- .../engine-server/src/__tests__/fixtures/slots-nested/index.js | 3 ++- .../src/__tests__/fixtures/slots-unknown/index.js | 3 ++- .../@lwc/engine-server/src/__tests__/fixtures/slots/index.js | 3 ++- .../fixtures/styles-escaping-invalid-multiple-levels/index.js | 3 ++- .../styles-escaping-invalid-trailing-close-tag/index.js | 3 ++- .../styles-escaping-invalid-trailing-open-tag/index.js | 3 ++- .../styles-escaping-invalid-unclosed-open-tag/index.js | 3 ++- .../src/__tests__/fixtures/styles-escaping-safe/index.js | 3 ++- .../src/__tests__/fixtures/styles-escaping-unsafe/index.js | 3 ++- .../src/__tests__/fixtures/styles-keyframes/index.js | 3 ++- .../src/__tests__/fixtures/styles-shared/index.js | 1 + .../@lwc/engine-server/src/__tests__/fixtures/styles/index.js | 3 ++- .../@lwc/engine-server/src/__tests__/fixtures/svgs/index.js | 3 ++- .../engine-server/src/__tests__/fixtures/tag-name/index.js | 1 + .../src/__tests__/fixtures/text-interpolation-escape/index.js | 3 ++- .../src/__tests__/fixtures/text-interpolation/index.js | 3 ++- .../engine-server/src/__tests__/fixtures/text-static/index.js | 3 ++- .../@lwc/engine-server/src/__tests__/fixtures/wire/index.js | 3 ++- 89 files changed, 157 insertions(+), 68 deletions(-) diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-aria-modify/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-aria-modify/index.js index 76abf71393..55d4302e11 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-aria-modify/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-aria-modify/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-cmp'; export { default } from 'x/cmp'; +export * from 'x/cmp'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-aria/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-aria/index.js index 51ac66cc15..d5a55ceefa 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-aria/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-aria/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-parent'; export { default } from 'x/parent'; +export * from 'x/parent'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-boolean/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-boolean/index.js index 4e8daf88a0..e1bbf136a8 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-boolean/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-boolean/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-attribute-boolean'; -export { default } from 'x/attribute-boolean'; \ No newline at end of file +export { default } from 'x/attribute-boolean'; +export * from 'x/attribute-boolean'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-component-aria/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-component-aria/index.js index c76387de4d..30f0f1dad1 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-component-aria/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-component-aria/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-attribute-component-aria'; -export { default } from 'x/attribute-component-aria'; \ No newline at end of file +export { default } from 'x/attribute-component-aria'; +export * from 'x/attribute-component-aria'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-component-global-html/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-component-global-html/index.js index 8d0fa398cd..35f32298fe 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-component-global-html/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-component-global-html/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-attribute-component-global-html'; -export { default } from 'x/attribute-component-global-html'; \ No newline at end of file +export { default } from 'x/attribute-component-global-html'; +export * from 'x/attribute-component-global-html'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-dynamic-escape/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-dynamic-escape/index.js index 4ba9611652..17091e6944 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-dynamic-escape/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-dynamic-escape/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-attribute-dynamic-escape'; -export { default } from 'x/attribute-dynamic-escape'; \ No newline at end of file +export { default } from 'x/attribute-dynamic-escape'; +export * from 'x/attribute-dynamic-escape'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-dynamic-with-scoped-css/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-dynamic-with-scoped-css/index.js index f6247d4521..adaa433504 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-dynamic-with-scoped-css/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-dynamic-with-scoped-css/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-attribute-dynamic-with-scoped-css'; -export { default } from 'x/attribute-dynamic-with-scoped-css'; \ No newline at end of file +export { default } from 'x/attribute-dynamic-with-scoped-css'; +export * from 'x/attribute-dynamic-with-scoped-css'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-dynamic/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-dynamic/index.js index 77a3dd537d..ae3a1deced 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-dynamic/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-dynamic/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-attribute-dynamic'; -export { default } from 'x/attribute-dynamic'; \ No newline at end of file +export { default } from 'x/attribute-dynamic'; +export * from 'x/attribute-dynamic'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-global-html/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-global-html/index.js index 06c5a00dfa..c79be2d623 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-global-html/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-global-html/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-attribute-global-html'; -export { default } from 'x/attribute-global-html'; \ No newline at end of file +export { default } from 'x/attribute-global-html'; +export * from 'x/attribute-global-html'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-live-bindings/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-live-bindings/index.js index 16dda308db..7bb7419ae6 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-live-bindings/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-live-bindings/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-attribute-live-bindings'; -export { default } from 'x/attribute-live-bindings'; \ No newline at end of file +export { default } from 'x/attribute-live-bindings'; +export * from 'x/attribute-live-bindings'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-static/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-static/index.js index 36447ddc38..ea02422e8a 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-static/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-static/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-attribute-static'; -export { default } from 'x/attribute-static'; \ No newline at end of file +export { default } from 'x/attribute-static'; +export * from 'x/attribute-static'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-style/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-style/index.js index abff73d484..d11d651354 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-style/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/attribute-style/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-attribute-style'; -export { default } from 'x/attribute-style'; \ No newline at end of file +export { default } from 'x/attribute-style'; +export * from 'x/attribute-style'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/attributes-aria/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/attributes-aria/index.js index 62b38f9420..693e047534 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/attributes-aria/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/attributes-aria/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-attributes-aria'; -export { default } from 'x/attributes-aria'; \ No newline at end of file +export { default } from 'x/attributes-aria'; +export * from 'x/attributes-aria'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/comments-basic/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/comments-basic/index.js index edea06bbd8..7e563d21ce 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/comments-basic/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/comments-basic/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-comments-basic'; -export { default } from 'x/comments-basic'; \ No newline at end of file +export { default } from 'x/comments-basic'; +export * from 'x/comments-basic'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/comments-foreach/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/comments-foreach/index.js index b55c6973b3..a230d24210 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/comments-foreach/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/comments-foreach/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-comments-foreach'; -export { default } from 'x/comments-foreach'; \ No newline at end of file +export { default } from 'x/comments-foreach'; +export * from 'x/comments-foreach'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/comments-if/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/comments-if/index.js index 7b5a595c3c..edd7754fee 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/comments-if/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/comments-if/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-comments-if'; -export { default } from 'x/comments-if'; \ No newline at end of file +export { default } from 'x/comments-if'; +export * from 'x/comments-if'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/comments-slot/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/comments-slot/index.js index 25239527e7..096e5dfc6c 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/comments-slot/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/comments-slot/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-comments-slot'; -export { default } from 'x/comments-slot'; \ No newline at end of file +export { default } from 'x/comments-slot'; +export * from 'x/comments-slot'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/component/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/component/index.js index 366f03796c..f385012e94 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/component/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/component/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-component'; -export { default } from 'x/component'; \ No newline at end of file +export { default } from 'x/component'; +export * from 'x/component'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/computed/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/computed/index.js index 22951e4138..5f0b68009e 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/computed/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/computed/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-computed'; -export { default } from 'x/computed'; \ No newline at end of file +export { default } from 'x/computed'; +export * from 'x/computed'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/context-deep/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/context-deep/index.js index 88f16af4d7..94cb0e9241 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/context-deep/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/context-deep/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-provider'; export { default } from 'x/provider'; +export * from 'x/provider'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/context-installation-on-non-element/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/context-installation-on-non-element/index.js index 88f16af4d7..94cb0e9241 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/context-installation-on-non-element/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/context-installation-on-non-element/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-provider'; export { default } from 'x/provider'; +export * from 'x/provider'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/context-nested/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/context-nested/index.js index 88f16af4d7..94cb0e9241 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/context-nested/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/context-nested/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-provider'; export { default } from 'x/provider'; +export * from 'x/provider'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/context-no-provider/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/context-no-provider/index.js index 51ac66cc15..d5a55ceefa 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/context-no-provider/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/context-no-provider/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-parent'; export { default } from 'x/parent'; +export * from 'x/parent'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/context-shadowed/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/context-shadowed/index.js index 88f16af4d7..94cb0e9241 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/context-shadowed/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/context-shadowed/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-provider'; export { default } from 'x/provider'; +export * from 'x/provider'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/context-simple/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/context-simple/index.js index 88f16af4d7..94cb0e9241 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/context-simple/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/context-simple/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-provider'; export { default } from 'x/provider'; +export * from 'x/provider'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/context-slotted/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/context-slotted/index.js index ee7122ff1e..64dd80573e 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/context-slotted/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/context-slotted/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-root'; export { default } from 'x/root'; +export * from 'x/root'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/custom-styles-with-newlines/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/custom-styles-with-newlines/index.js index 366f03796c..f385012e94 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/custom-styles-with-newlines/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/custom-styles-with-newlines/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-component'; -export { default } from 'x/component'; \ No newline at end of file +export { default } from 'x/component'; +export * from 'x/component'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/delegates-focus/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/delegates-focus/index.js index 34cca779a0..3a85f0d7ed 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/delegates-focus/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/delegates-focus/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-delegates-focus'; -export { default } from 'x/delegates-focus'; \ No newline at end of file +export { default } from 'x/delegates-focus'; +export * from 'x/delegates-focus'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/directive-external/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/directive-external/index.js index ec1266b17a..bf9eb21437 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/directive-external/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/directive-external/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-test'; export { default } from 'x/test'; +export * from 'x/test'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/dynamic-component-no-ctor/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/dynamic-component-no-ctor/index.js index b9ea6efc1b..ad94976f6d 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/dynamic-component-no-ctor/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/dynamic-component-no-ctor/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-dynamic-component-no-ctor'; -export { default } from 'x/dynamic-no-ctor'; \ No newline at end of file +export { default } from 'x/dynamic-no-ctor'; +export * from 'x/dynamic-no-ctor'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/dynamic-components/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/dynamic-components/index.js index 6523e5b001..6192730f7d 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/dynamic-components/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/dynamic-components/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-dynamic-component'; -export { default } from 'x/dynamic'; \ No newline at end of file +export { default } from 'x/dynamic'; +export * from 'x/dynamic'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/dynamic-slots/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/dynamic-slots/index.js index 8d091cb205..e7790a029e 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/dynamic-slots/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/dynamic-slots/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-slots'; -export { default } from 'x/slots'; \ No newline at end of file +export { default } from 'x/slots'; +export * from 'x/slots'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/face-callback/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/face-callback/index.js index 0944c83824..61e7bf8749 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/face-callback/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/face-callback/index.js @@ -1,2 +1,3 @@ export const tagName = 'face-container'; -export { default } from 'face/container'; \ No newline at end of file +export { default } from 'face/container'; +export * from 'face/container'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/for-each-block/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/for-each-block/index.js index 3e6abe5016..05bba692d3 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/for-each-block/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/for-each-block/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-for-each-block'; -export { default } from 'x/for-each-block'; \ No newline at end of file +export { default } from 'x/for-each-block'; +export * from 'x/for-each-block'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/for-each-child-nested/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/for-each-child-nested/index.js index c11c2ee62e..5e39fc2d05 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/for-each-child-nested/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/for-each-child-nested/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-for-each-child-nested'; -export { default } from 'x/for-each-child-nested'; \ No newline at end of file +export { default } from 'x/for-each-child-nested'; +export * from 'x/for-each-child-nested'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/for-each-nested/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/for-each-nested/index.js index ab98b84778..df281f1b93 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/for-each-nested/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/for-each-nested/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-for-each-nested'; -export { default } from 'x/for-each-nested'; \ No newline at end of file +export { default } from 'x/for-each-nested'; +export * from 'x/for-each-nested'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/for-static-content/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/for-static-content/index.js index 714cdc4f7c..9763122105 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/for-static-content/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/for-static-content/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-container'; export { default } from 'x/container'; +export * from 'x/container'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/getter-class-list/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/getter-class-list/index.js index 4c23532df9..0399a81a12 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/getter-class-list/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/getter-class-list/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-getter-class-list'; -export { default } from 'x/getter-class-list'; \ No newline at end of file +export { default } from 'x/getter-class-list'; +export * from 'x/getter-class-list'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/getter-is-connected/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/getter-is-connected/index.js index a6ec2b3204..39d1651015 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/getter-is-connected/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/getter-is-connected/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-getter-is-connected'; -export { default } from 'x/getter-is-connected'; \ No newline at end of file +export { default } from 'x/getter-is-connected'; +export * from 'x/getter-is-connected'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/if-block/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/if-block/index.js index 8ec90fd2be..04a31cdc00 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/if-block/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/if-block/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-if-block'; -export { default } from 'x/if-block'; \ No newline at end of file +export { default } from 'x/if-block'; +export * from 'x/if-block'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/if-conditional-slot-content/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/if-conditional-slot-content/index.js index 8a480a7c86..5dd04a0cef 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/if-conditional-slot-content/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/if-conditional-slot-content/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-if-conditional-slot-content'; -export { default } from 'x/if-conditional-slot-content'; \ No newline at end of file +export { default } from 'x/if-conditional-slot-content'; +export * from 'x/if-conditional-slot-content'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/if-conditional-slot/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/if-conditional-slot/index.js index d29c70bdac..61074004ee 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/if-conditional-slot/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/if-conditional-slot/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-if-conditional-slot'; -export { default } from 'x/if-conditional-slot'; \ No newline at end of file +export { default } from 'x/if-conditional-slot'; +export * from 'x/if-conditional-slot'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/if-custom-element-child/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/if-custom-element-child/index.js index 6ff7f0621e..118a10b5d3 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/if-custom-element-child/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/if-custom-element-child/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-if-custom-element-child'; -export { default } from 'x/if-custom-element-child'; \ No newline at end of file +export { default } from 'x/if-custom-element-child'; +export * from 'x/if-custom-element-child'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/if-element-child/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/if-element-child/index.js index c25d719cde..edaff8e9f0 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/if-element-child/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/if-element-child/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-if-element-child'; -export { default } from 'x/if-element-child'; \ No newline at end of file +export { default } from 'x/if-element-child'; +export * from 'x/if-element-child'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/iterator-block/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/iterator-block/index.js index d525dd2d5b..2b652b5533 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/iterator-block/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/iterator-block/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-iterator-block'; -export { default } from 'x/iterator-block'; \ No newline at end of file +export { default } from 'x/iterator-block'; +export * from 'x/iterator-block'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/lifecycle-hooks/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/lifecycle-hooks/index.js index e7123d6ab0..a3f4e421ba 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/lifecycle-hooks/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/lifecycle-hooks/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-lifecycle-hooks'; -export { default } from 'x/lifecycle-hooks'; \ No newline at end of file +export { default } from 'x/lifecycle-hooks'; +export * from 'x/lifecycle-hooks'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/light-dom-multiple/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/light-dom-multiple/index.js index 51ac66cc15..d5a55ceefa 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/light-dom-multiple/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/light-dom-multiple/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-parent'; export { default } from 'x/parent'; +export * from 'x/parent'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/light-dom-scoped-styles/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/light-dom-scoped-styles/index.js index 92ae69ac24..12a4de763d 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/light-dom-scoped-styles/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/light-dom-scoped-styles/index.js @@ -1,3 +1,4 @@ export const tagName = 'x-basic'; export { default } from 'x/basic'; +export * from 'x/basic'; export const features = []; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/light-dom/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/light-dom/index.js index 48aa094adb..3bcf4a5f81 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/light-dom/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/light-dom/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-basic'; -export { default } from 'x/basic'; \ No newline at end of file +export { default } from 'x/basic'; +export * from 'x/basic'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/lwc-dom-manual/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/lwc-dom-manual/index.js index 933670c81f..cb8930c9d2 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/lwc-dom-manual/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/lwc-dom-manual/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-lwc-dom-manual'; -export { default } from 'x/lwc-dom-manual'; \ No newline at end of file +export { default } from 'x/lwc-dom-manual'; +export * from 'x/lwc-dom-manual'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/lwc-dynamic/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/lwc-dynamic/index.js index bf0710c327..c3322a27ec 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/lwc-dynamic/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/lwc-dynamic/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-lwc-dynamic'; -export { default } from 'x/lwc-dynamic'; \ No newline at end of file +export { default } from 'x/lwc-dynamic'; +export * from 'x/lwc-dynamic'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/lwc-inner-html/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/lwc-inner-html/index.js index b2f8e2a6fc..3aa8dde472 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/lwc-inner-html/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/lwc-inner-html/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-lwc-inner-html'; -export { default } from 'x/lwc-inner-html'; \ No newline at end of file +export { default } from 'x/lwc-inner-html'; +export * from 'x/lwc-inner-html'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/method-get-attribute/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/method-get-attribute/index.js index 43b59fe0d0..f0d7ea20e7 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/method-get-attribute/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/method-get-attribute/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-method-get-attribute'; -export { default } from 'x/method-get-attribute'; \ No newline at end of file +export { default } from 'x/method-get-attribute'; +export * from 'x/method-get-attribute'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/method-remove-attribute/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/method-remove-attribute/index.js index c177aef318..6a16299100 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/method-remove-attribute/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/method-remove-attribute/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-method-remove-attribute'; -export { default } from 'x/method-remove-attribute'; \ No newline at end of file +export { default } from 'x/method-remove-attribute'; +export * from 'x/method-remove-attribute'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/method-set-attribute/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/method-set-attribute/index.js index b57d587d5f..1fa63e0eb5 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/method-set-attribute/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/method-set-attribute/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-method-set-attribute'; -export { default } from 'x/method-set-attribute'; \ No newline at end of file +export { default } from 'x/method-set-attribute'; +export * from 'x/method-set-attribute'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/methods-noop/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/methods-noop/index.js index 1ce9874eba..0f0109b1bf 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/methods-noop/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/methods-noop/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-methods-noop'; -export { default } from 'x/methods-noop'; \ No newline at end of file +export { default } from 'x/methods-noop'; +export * from 'x/methods-noop'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/methods-unsupported/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/methods-unsupported/index.js index 5fdab1f8e4..5a657d4461 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/methods-unsupported/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/methods-unsupported/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-methods-unsupported'; -export { default } from 'x/methods-unsupported'; \ No newline at end of file +export { default } from 'x/methods-unsupported'; +export * from 'x/methods-unsupported'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/no-boundary-protection-basic/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/no-boundary-protection-basic/index.js index 51ac66cc15..d5a55ceefa 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/no-boundary-protection-basic/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/no-boundary-protection-basic/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-parent'; export { default } from 'x/parent'; +export * from 'x/parent'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/no-boundary-protection-child/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/no-boundary-protection-child/index.js index 51ac66cc15..d5a55ceefa 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/no-boundary-protection-child/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/no-boundary-protection-child/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-parent'; export { default } from 'x/parent'; +export * from 'x/parent'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/no-boundary-protection-deep-child/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/no-boundary-protection-deep-child/index.js index 51ac66cc15..d5a55ceefa 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/no-boundary-protection-deep-child/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/no-boundary-protection-deep-child/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-parent'; export { default } from 'x/parent'; +export * from 'x/parent'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/no-boundary-protection-slotted/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/no-boundary-protection-slotted/index.js index 51ac66cc15..d5a55ceefa 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/no-boundary-protection-slotted/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/no-boundary-protection-slotted/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-parent'; export { default } from 'x/parent'; +export * from 'x/parent'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/overwrite-properties-template/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/overwrite-properties-template/index.js index 45254be6c7..32352608a5 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/overwrite-properties-template/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/overwrite-properties-template/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-overwrite-properties-template'; -export { default } from 'x/overwrite-properties-template'; \ No newline at end of file +export { default } from 'x/overwrite-properties-template'; +export * from 'x/overwrite-properties-template'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/overwrite-properties/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/overwrite-properties/index.js index 0f1b4c93e2..a2dfbd82f7 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/overwrite-properties/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/overwrite-properties/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-overwrite-properties'; -export { default } from 'x/overwrite-properties'; \ No newline at end of file +export { default } from 'x/overwrite-properties'; +export * from 'x/overwrite-properties'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/parent-child-connected-callback/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/parent-child-connected-callback/index.js index 51ac66cc15..d5a55ceefa 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/parent-child-connected-callback/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/parent-child-connected-callback/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-parent'; export { default } from 'x/parent'; +export * from 'x/parent'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/parent-child-read-only-prop/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/parent-child-read-only-prop/index.js index 51ac66cc15..d5a55ceefa 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/parent-child-read-only-prop/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/parent-child-read-only-prop/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-parent'; export { default } from 'x/parent'; +export * from 'x/parent'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/rehydration/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/rehydration/index.js index 45ab7335a6..1906d703fb 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/rehydration/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/rehydration/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-rehydration'; -export { default } from 'x/rehydration'; \ No newline at end of file +export { default } from 'x/rehydration'; +export * from 'x/rehydration'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/scoped-slots/basic/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/scoped-slots/basic/index.js index aa97f8ed82..2aaecb29a4 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/scoped-slots/basic/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/scoped-slots/basic/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-basic-parent'; -export { default } from 'x/basicParent'; \ No newline at end of file +export { default } from 'x/basicParent'; +export * from 'x/basicParent'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/scoped-slots/for-each/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/scoped-slots/for-each/index.js index 8de3fb8bf8..bb358b20d2 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/scoped-slots/for-each/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/scoped-slots/for-each/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-parent-of-child-with-for-each'; -export { default } from 'x/parentOfChildWithForEach'; \ No newline at end of file +export { default } from 'x/parentOfChildWithForEach'; +export * from 'x/parentOfChildWithForEach'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/scoped-slots/named-slot/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/scoped-slots/named-slot/index.js index 16d88f002e..229381c6da 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/scoped-slots/named-slot/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/scoped-slots/named-slot/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-parent'; -export { default } from 'x/parent'; \ No newline at end of file +export { default } from 'x/parent'; +export * from 'x/parent'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/slotchange/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/slotchange/index.js index 03bfce4301..a4cc256e70 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/slotchange/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/slotchange/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-slotchange'; -export { default } from 'x/slotchange'; \ No newline at end of file +export { default } from 'x/slotchange'; +export * from 'x/slotchange'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/slots-fallback/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/slots-fallback/index.js index b356c9e97b..a5c08d4b1b 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/slots-fallback/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/slots-fallback/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-slots-fallback'; -export { default } from 'x/slots-fallback'; \ No newline at end of file +export { default } from 'x/slots-fallback'; +export * from 'x/slots-fallback'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/slots-nested/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/slots-nested/index.js index ecd74401b8..6bd31241ec 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/slots-nested/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/slots-nested/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-slots-nested'; -export { default } from 'x/slots-nested'; \ No newline at end of file +export { default } from 'x/slots-nested'; +export * from 'x/slots-nested'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/slots-unknown/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/slots-unknown/index.js index e8b1c818db..0ea1e82521 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/slots-unknown/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/slots-unknown/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-slots-unknown'; -export { default } from 'x/slots-unknown'; \ No newline at end of file +export { default } from 'x/slots-unknown'; +export * from 'x/slots-unknown'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/slots/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/slots/index.js index 8d091cb205..e7790a029e 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/slots/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/slots/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-slots'; -export { default } from 'x/slots'; \ No newline at end of file +export { default } from 'x/slots'; +export * from 'x/slots'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-invalid-multiple-levels/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-invalid-multiple-levels/index.js index 2f6dd19641..715a68f1cc 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-invalid-multiple-levels/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-invalid-multiple-levels/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-styles'; -export { default } from 'x/styles'; \ No newline at end of file +export { default } from 'x/styles'; +export * from 'x/styles'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-invalid-trailing-close-tag/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-invalid-trailing-close-tag/index.js index 2f6dd19641..715a68f1cc 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-invalid-trailing-close-tag/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-invalid-trailing-close-tag/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-styles'; -export { default } from 'x/styles'; \ No newline at end of file +export { default } from 'x/styles'; +export * from 'x/styles'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-invalid-trailing-open-tag/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-invalid-trailing-open-tag/index.js index 2f6dd19641..715a68f1cc 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-invalid-trailing-open-tag/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-invalid-trailing-open-tag/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-styles'; -export { default } from 'x/styles'; \ No newline at end of file +export { default } from 'x/styles'; +export * from 'x/styles'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-invalid-unclosed-open-tag/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-invalid-unclosed-open-tag/index.js index 2f6dd19641..715a68f1cc 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-invalid-unclosed-open-tag/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-invalid-unclosed-open-tag/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-styles'; -export { default } from 'x/styles'; \ No newline at end of file +export { default } from 'x/styles'; +export * from 'x/styles'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-safe/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-safe/index.js index 2f6dd19641..715a68f1cc 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-safe/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-safe/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-styles'; -export { default } from 'x/styles'; \ No newline at end of file +export { default } from 'x/styles'; +export * from 'x/styles'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-unsafe/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-unsafe/index.js index 2f6dd19641..715a68f1cc 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-unsafe/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/styles-escaping-unsafe/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-styles'; -export { default } from 'x/styles'; \ No newline at end of file +export { default } from 'x/styles'; +export * from 'x/styles'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/styles-keyframes/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/styles-keyframes/index.js index 2f6dd19641..715a68f1cc 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/styles-keyframes/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/styles-keyframes/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-styles'; -export { default } from 'x/styles'; \ No newline at end of file +export { default } from 'x/styles'; +export * from 'x/styles'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/styles-shared/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/styles-shared/index.js index 51ac66cc15..d5a55ceefa 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/styles-shared/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/styles-shared/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-parent'; export { default } from 'x/parent'; +export * from 'x/parent'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/styles/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/styles/index.js index 2f6dd19641..715a68f1cc 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/styles/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/styles/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-styles'; -export { default } from 'x/styles'; \ No newline at end of file +export { default } from 'x/styles'; +export * from 'x/styles'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/svgs/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/svgs/index.js index d78ab6613b..28438f2ce1 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/svgs/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/svgs/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-svgs'; -export { default } from 'x/svgs'; \ No newline at end of file +export { default } from 'x/svgs'; +export * from 'x/svgs'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/tag-name/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/tag-name/index.js index ec5e6d6240..3921947682 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/tag-name/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/tag-name/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-tag-name'; export { default } from 'x/tag-name'; +export * from 'x/tag-name'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/text-interpolation-escape/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/text-interpolation-escape/index.js index 26df1d5a29..09bfc31739 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/text-interpolation-escape/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/text-interpolation-escape/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-text-interpolation-escape'; -export { default } from 'x/text-interpolation-escape'; \ No newline at end of file +export { default } from 'x/text-interpolation-escape'; +export * from 'x/text-interpolation-escape'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/text-interpolation/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/text-interpolation/index.js index 8346bbedc5..8f4a18930f 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/text-interpolation/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/text-interpolation/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-text-interpolation'; -export { default } from 'x/text-interpolation'; \ No newline at end of file +export { default } from 'x/text-interpolation'; +export * from 'x/text-interpolation'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/text-static/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/text-static/index.js index b6bf3f0d1d..28d2c1e65c 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/text-static/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/text-static/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-text-static'; -export { default } from 'x/text-static'; \ No newline at end of file +export { default } from 'x/text-static'; +export * from 'x/text-static'; \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/wire/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/index.js index a36c14a044..70aed90174 100644 --- a/packages/@lwc/engine-server/src/__tests__/fixtures/wire/index.js +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/wire/index.js @@ -1,2 +1,3 @@ export const tagName = 'x-wire'; -export { default } from 'x/wire'; \ No newline at end of file +export { default } from 'x/wire'; +export * from 'x/wire'; \ No newline at end of file From a5425a2be06051eed526a200caa3d446cb37ee75 Mon Sep 17 00:00:00 2001 From: Dale Bustad Date: Tue, 6 Feb 2024 02:01:15 -0800 Subject: [PATCH 02/13] chore: add package-local test command --- packages/@lwc/aria-reflection/package.json | 3 ++- packages/@lwc/babel-plugin-component/package.json | 3 ++- packages/@lwc/compiler/package.json | 3 ++- packages/@lwc/engine-core/package.json | 3 ++- packages/@lwc/engine-dom/package.json | 3 ++- packages/@lwc/engine-server/package.json | 3 ++- packages/@lwc/errors/package.json | 3 ++- packages/@lwc/features/package.json | 3 ++- packages/@lwc/module-resolver/package.json | 3 ++- packages/@lwc/rollup-plugin/package.json | 3 ++- packages/@lwc/shared/package.json | 3 ++- packages/@lwc/style-compiler/package.json | 3 ++- packages/@lwc/synthetic-shadow/package.json | 3 ++- packages/@lwc/template-compiler/package.json | 3 ++- packages/@lwc/wire-service/package.json | 3 ++- scripts/tasks/check-and-rewrite-package-json.js | 2 ++ 16 files changed, 32 insertions(+), 15 deletions(-) diff --git a/packages/@lwc/aria-reflection/package.json b/packages/@lwc/aria-reflection/package.json index 6e20db25fb..9163727a34 100644 --- a/packages/@lwc/aria-reflection/package.json +++ b/packages/@lwc/aria-reflection/package.json @@ -34,7 +34,8 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", + "test": "jest ./src/**/*.spec.ts" }, "nx": { "targets": { diff --git a/packages/@lwc/babel-plugin-component/package.json b/packages/@lwc/babel-plugin-component/package.json index 9298cac332..56a45d9026 100644 --- a/packages/@lwc/babel-plugin-component/package.json +++ b/packages/@lwc/babel-plugin-component/package.json @@ -30,7 +30,8 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", + "test": "jest ./src/**/*.spec.ts" }, "nx": { "targets": { diff --git a/packages/@lwc/compiler/package.json b/packages/@lwc/compiler/package.json index f31540feb4..8b89e03125 100755 --- a/packages/@lwc/compiler/package.json +++ b/packages/@lwc/compiler/package.json @@ -30,7 +30,8 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", + "test": "jest ./src/**/*.spec.ts" }, "nx": { "targets": { diff --git a/packages/@lwc/engine-core/package.json b/packages/@lwc/engine-core/package.json index 72b12ca945..9022104238 100644 --- a/packages/@lwc/engine-core/package.json +++ b/packages/@lwc/engine-core/package.json @@ -30,7 +30,8 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", + "test": "jest ./src/**/*.spec.ts" }, "nx": { "targets": { diff --git a/packages/@lwc/engine-dom/package.json b/packages/@lwc/engine-dom/package.json index a334db7c7d..6410389777 100644 --- a/packages/@lwc/engine-dom/package.json +++ b/packages/@lwc/engine-dom/package.json @@ -30,7 +30,8 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", + "test": "jest ./src/**/*.spec.ts" }, "nx": { "targets": { diff --git a/packages/@lwc/engine-server/package.json b/packages/@lwc/engine-server/package.json index a92ca7b780..dac568a845 100644 --- a/packages/@lwc/engine-server/package.json +++ b/packages/@lwc/engine-server/package.json @@ -30,7 +30,8 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", + "test": "jest ./src/**/*.spec.ts" }, "nx": { "targets": { diff --git a/packages/@lwc/errors/package.json b/packages/@lwc/errors/package.json index 41f03de9e4..a5505532cb 100644 --- a/packages/@lwc/errors/package.json +++ b/packages/@lwc/errors/package.json @@ -30,7 +30,8 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", + "test": "jest ./src/**/*.spec.ts" }, "nx": { "targets": { diff --git a/packages/@lwc/features/package.json b/packages/@lwc/features/package.json index 05996b0aa5..a21ec501d5 100644 --- a/packages/@lwc/features/package.json +++ b/packages/@lwc/features/package.json @@ -30,7 +30,8 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", + "test": "jest ./src/**/*.spec.ts" }, "nx": { "targets": { diff --git a/packages/@lwc/module-resolver/package.json b/packages/@lwc/module-resolver/package.json index f7ac4c57de..bee3982ac3 100644 --- a/packages/@lwc/module-resolver/package.json +++ b/packages/@lwc/module-resolver/package.json @@ -30,7 +30,8 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", + "test": "jest ./src/**/*.spec.ts" }, "nx": { "targets": { diff --git a/packages/@lwc/rollup-plugin/package.json b/packages/@lwc/rollup-plugin/package.json index 4f1da7a7db..3f9d0c2e9a 100644 --- a/packages/@lwc/rollup-plugin/package.json +++ b/packages/@lwc/rollup-plugin/package.json @@ -30,7 +30,8 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", + "test": "jest ./src/**/*.spec.ts" }, "nx": { "targets": { diff --git a/packages/@lwc/shared/package.json b/packages/@lwc/shared/package.json index 039ed6a818..350923974c 100644 --- a/packages/@lwc/shared/package.json +++ b/packages/@lwc/shared/package.json @@ -30,7 +30,8 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", + "test": "jest ./src/**/*.spec.ts" }, "nx": { "targets": { diff --git a/packages/@lwc/style-compiler/package.json b/packages/@lwc/style-compiler/package.json index ff56c45b6a..e4b37deccf 100644 --- a/packages/@lwc/style-compiler/package.json +++ b/packages/@lwc/style-compiler/package.json @@ -30,7 +30,8 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", + "test": "jest ./src/**/*.spec.ts" }, "nx": { "targets": { diff --git a/packages/@lwc/synthetic-shadow/package.json b/packages/@lwc/synthetic-shadow/package.json index 9521d7c635..2b8eae8362 100644 --- a/packages/@lwc/synthetic-shadow/package.json +++ b/packages/@lwc/synthetic-shadow/package.json @@ -30,7 +30,8 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", + "test": "jest ./src/**/*.spec.ts" }, "nx": { "targets": { diff --git a/packages/@lwc/template-compiler/package.json b/packages/@lwc/template-compiler/package.json index de86820709..cb285d1931 100644 --- a/packages/@lwc/template-compiler/package.json +++ b/packages/@lwc/template-compiler/package.json @@ -30,7 +30,8 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", + "test": "jest ./src/**/*.spec.ts" }, "nx": { "targets": { diff --git a/packages/@lwc/wire-service/package.json b/packages/@lwc/wire-service/package.json index 61b88bd2fb..31c52f7199 100644 --- a/packages/@lwc/wire-service/package.json +++ b/packages/@lwc/wire-service/package.json @@ -30,7 +30,8 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", + "test": "jest ./src/**/*.spec.ts" }, "nx": { "targets": { diff --git a/scripts/tasks/check-and-rewrite-package-json.js b/scripts/tasks/check-and-rewrite-package-json.js index e0931434fe..ef99970b57 100644 --- a/scripts/tasks/check-and-rewrite-package-json.js +++ b/scripts/tasks/check-and-rewrite-package-json.js @@ -81,6 +81,8 @@ for (const dir of directories) { scripts: { build: 'rollup --config ../../../scripts/rollup/rollup.config.js', dev: 'rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen', + test: name === '@lwc/ssr-compiler' ? undefined : 'jest ./src/**/*.spec.ts', + 'test-manual': name === '@lwc/ssr-compiler' ? 'jest ./src/**/*.spec.ts' : undefined, }, nx: { targets: { From 3b256d666ef00a1a6590c924d5025ee3a07dc375 Mon Sep 17 00:00:00 2001 From: Dale Bustad Date: Tue, 6 Feb 2024 02:02:28 -0800 Subject: [PATCH 03/13] feat: experimental SSR compiler --- packages/@lwc/compiler/package.json | 1 + packages/@lwc/compiler/src/index.ts | 1 + packages/@lwc/compiler/src/options.ts | 2 + .../compiler/src/transformers/transformer.ts | 5 +- .../fixtures/deeply-nested/expected.html | 1231 +++++++++++++++++ .../__tests__/fixtures/deeply-nested/index.js | 3 + .../modules/x/component/component.html | 25 + .../modules/x/component/component.js | 26 + packages/@lwc/rollup-plugin/src/index.ts | 4 + packages/@lwc/ssr-compiler/jest.config.js | 12 + packages/@lwc/ssr-compiler/package.json | 59 + .../src/__tests__/fixtures.spec.ts | 187 +++ .../src/compile-js/catalog-tmpls.ts | 26 + .../src/compile-js/generate-markup.ts | 123 ++ .../@lwc/ssr-compiler/src/compile-js/index.ts | 152 ++ .../ssr-compiler/src/compile-js/lwc-import.ts | 32 + .../src/compile-js/stylesheets.ts | 59 + .../@lwc/ssr-compiler/src/compile-js/types.ts | 28 + .../src/compile-template/comment.ts | 19 + .../src/compile-template/component.ts | 96 ++ .../src/compile-template/context.ts | 79 ++ .../src/compile-template/element.ts | 106 ++ .../src/compile-template/expression.ts | 48 + .../src/compile-template/for-each.ts | 68 + .../ssr-compiler/src/compile-template/if.ts | 65 + .../src/compile-template/index.ts | 85 ++ .../src/compile-template/ir-to-es.ts | 53 + .../src/compile-template/shared.ts | 110 ++ .../ssr-compiler/src/compile-template/text.ts | 63 + .../src/compile-template/types.ts | 27 + packages/@lwc/ssr-compiler/src/estemplate.ts | 167 +++ .../@lwc/ssr-compiler/src/estree/builders.ts | 18 + .../ssr-compiler/src/estree/validators.ts | 28 + packages/@lwc/ssr-compiler/src/generator.ts | 17 + packages/@lwc/ssr-compiler/src/index.ts | 53 + packages/@lwc/ssr-compiler/src/optimizer.ts | 12 + packages/@lwc/ssr-compiler/src/shared.ts | 66 + packages/@lwc/ssr-compiler/tsconfig.json | 5 + packages/@lwc/ssr-runtime/jest.config.js | 12 + packages/@lwc/ssr-runtime/package.json | 46 + packages/@lwc/ssr-runtime/src/index.ts | 161 +++ packages/@lwc/ssr-runtime/tsconfig.json | 9 + packages/@lwc/template-compiler/src/index.ts | 3 + scripts/rollup/rollup.config.js | 4 + yarn.lock | 55 +- 45 files changed, 3440 insertions(+), 11 deletions(-) create mode 100644 packages/@lwc/engine-server/src/__tests__/fixtures/deeply-nested/expected.html create mode 100644 packages/@lwc/engine-server/src/__tests__/fixtures/deeply-nested/index.js create mode 100755 packages/@lwc/engine-server/src/__tests__/fixtures/deeply-nested/modules/x/component/component.html create mode 100755 packages/@lwc/engine-server/src/__tests__/fixtures/deeply-nested/modules/x/component/component.js create mode 100644 packages/@lwc/ssr-compiler/jest.config.js create mode 100644 packages/@lwc/ssr-compiler/package.json create mode 100644 packages/@lwc/ssr-compiler/src/__tests__/fixtures.spec.ts create mode 100644 packages/@lwc/ssr-compiler/src/compile-js/catalog-tmpls.ts create mode 100644 packages/@lwc/ssr-compiler/src/compile-js/generate-markup.ts create mode 100644 packages/@lwc/ssr-compiler/src/compile-js/index.ts create mode 100644 packages/@lwc/ssr-compiler/src/compile-js/lwc-import.ts create mode 100644 packages/@lwc/ssr-compiler/src/compile-js/stylesheets.ts create mode 100644 packages/@lwc/ssr-compiler/src/compile-js/types.ts create mode 100644 packages/@lwc/ssr-compiler/src/compile-template/comment.ts create mode 100644 packages/@lwc/ssr-compiler/src/compile-template/component.ts create mode 100644 packages/@lwc/ssr-compiler/src/compile-template/context.ts create mode 100644 packages/@lwc/ssr-compiler/src/compile-template/element.ts create mode 100644 packages/@lwc/ssr-compiler/src/compile-template/expression.ts create mode 100644 packages/@lwc/ssr-compiler/src/compile-template/for-each.ts create mode 100644 packages/@lwc/ssr-compiler/src/compile-template/if.ts create mode 100644 packages/@lwc/ssr-compiler/src/compile-template/index.ts create mode 100644 packages/@lwc/ssr-compiler/src/compile-template/ir-to-es.ts create mode 100644 packages/@lwc/ssr-compiler/src/compile-template/shared.ts create mode 100644 packages/@lwc/ssr-compiler/src/compile-template/text.ts create mode 100644 packages/@lwc/ssr-compiler/src/compile-template/types.ts create mode 100644 packages/@lwc/ssr-compiler/src/estemplate.ts create mode 100644 packages/@lwc/ssr-compiler/src/estree/builders.ts create mode 100644 packages/@lwc/ssr-compiler/src/estree/validators.ts create mode 100644 packages/@lwc/ssr-compiler/src/generator.ts create mode 100644 packages/@lwc/ssr-compiler/src/index.ts create mode 100644 packages/@lwc/ssr-compiler/src/optimizer.ts create mode 100644 packages/@lwc/ssr-compiler/src/shared.ts create mode 100644 packages/@lwc/ssr-compiler/tsconfig.json create mode 100644 packages/@lwc/ssr-runtime/jest.config.js create mode 100644 packages/@lwc/ssr-runtime/package.json create mode 100644 packages/@lwc/ssr-runtime/src/index.ts create mode 100644 packages/@lwc/ssr-runtime/tsconfig.json diff --git a/packages/@lwc/compiler/package.json b/packages/@lwc/compiler/package.json index 8b89e03125..7ac6d0d271 100755 --- a/packages/@lwc/compiler/package.json +++ b/packages/@lwc/compiler/package.json @@ -52,6 +52,7 @@ "@lwc/babel-plugin-component": "6.1.1", "@lwc/errors": "6.1.1", "@lwc/shared": "6.1.1", + "@lwc/ssr-compiler": "6.1.1", "@lwc/style-compiler": "6.1.1", "@lwc/template-compiler": "6.1.1" } diff --git a/packages/@lwc/compiler/src/index.ts b/packages/@lwc/compiler/src/index.ts index f3fa3608f0..40ece35d39 100755 --- a/packages/@lwc/compiler/src/index.ts +++ b/packages/@lwc/compiler/src/index.ts @@ -8,6 +8,7 @@ export { transform, transformSync } from './transformers/transformer'; export { TransformResult } from './transformers/transformer'; export { + NormalizedTransformOptions, TransformOptions, StylesheetConfig, CustomPropertiesResolution, diff --git a/packages/@lwc/compiler/src/options.ts b/packages/@lwc/compiler/src/options.ts index edcd781b6d..99dae4523e 100755 --- a/packages/@lwc/compiler/src/options.ts +++ b/packages/@lwc/compiler/src/options.ts @@ -103,6 +103,7 @@ export interface TransformOptions { enableLightningWebSecurityTransforms?: boolean; instrumentation?: InstrumentationObject; apiVersion?: number; + targetSSR?: boolean; } type RequiredTransformOptions = Omit< @@ -208,5 +209,6 @@ function normalizeOptions(options: TransformOptions): NormalizedTransformOptions outputConfig, experimentalDynamicComponent, apiVersion, + targetSSR: !!options.targetSSR, }; } diff --git a/packages/@lwc/compiler/src/transformers/transformer.ts b/packages/@lwc/compiler/src/transformers/transformer.ts index f03598f7e2..5c78b75451 100755 --- a/packages/@lwc/compiler/src/transformers/transformer.ts +++ b/packages/@lwc/compiler/src/transformers/transformer.ts @@ -13,6 +13,7 @@ import { invariant, CompilerDiagnostic, } from '@lwc/errors'; +import { compileComponentForSSR, compileTemplateForSSR } from '@lwc/ssr-compiler'; import { NormalizedTransformOptions, TransformOptions, validateTransformOptions } from '../options'; import styleTransform from './style'; @@ -76,7 +77,7 @@ function transformFile( switch (path.extname(filename)) { case '.html': - transformer = templateTransformer; + transformer = options.targetSSR ? compileTemplateForSSR : templateTransformer; break; case '.css': @@ -85,7 +86,7 @@ function transformFile( case '.ts': case '.js': - transformer = scriptTransformer; + transformer = options.targetSSR ? compileComponentForSSR : scriptTransformer; break; default: diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/deeply-nested/expected.html b/packages/@lwc/engine-server/src/__tests__/fixtures/deeply-nested/expected.html new file mode 100644 index 0000000000..33bead581c --- /dev/null +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/deeply-nested/expected.html @@ -0,0 +1,1231 @@ + + + \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/deeply-nested/index.js b/packages/@lwc/engine-server/src/__tests__/fixtures/deeply-nested/index.js new file mode 100644 index 0000000000..cd34090d2c --- /dev/null +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/deeply-nested/index.js @@ -0,0 +1,3 @@ +export const tagName = 'x-component'; +export { default } from 'x/component'; +export * from 'x/component'; diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/deeply-nested/modules/x/component/component.html b/packages/@lwc/engine-server/src/__tests__/fixtures/deeply-nested/modules/x/component/component.html new file mode 100755 index 0000000000..098e7a35ae --- /dev/null +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/deeply-nested/modules/x/component/component.html @@ -0,0 +1,25 @@ + \ No newline at end of file diff --git a/packages/@lwc/engine-server/src/__tests__/fixtures/deeply-nested/modules/x/component/component.js b/packages/@lwc/engine-server/src/__tests__/fixtures/deeply-nested/modules/x/component/component.js new file mode 100755 index 0000000000..fa42ae54bb --- /dev/null +++ b/packages/@lwc/engine-server/src/__tests__/fixtures/deeply-nested/modules/x/component/component.js @@ -0,0 +1,26 @@ +import { api, LightningElement } from 'lwc'; + +export default class Component extends LightningElement { + @api remaining = 9; + @api label = 'default'; + + get someChildren() { + return ['a', 'b', 'c']; + } + + get isPositive() { + return this.remaining > 0; + } + + get isDivisibleByThree() { + return (this.remaining % 3) === 0 + } + + get isDivisibleByTwo() { + return (this.remaining % 3) === 0 + } + + get minusOne() { + return this.remaining - 1; + } +} \ No newline at end of file diff --git a/packages/@lwc/rollup-plugin/src/index.ts b/packages/@lwc/rollup-plugin/src/index.ts index 41cbd14a0d..a369999c2d 100644 --- a/packages/@lwc/rollup-plugin/src/index.ts +++ b/packages/@lwc/rollup-plugin/src/index.ts @@ -16,6 +16,8 @@ import { APIVersion, getAPIVersionFromNumber } from '@lwc/shared'; import type { CompilerDiagnostic } from '@lwc/errors'; export interface RollupLwcOptions { + /** A boolean indicating whether to compile for SSR runtime target. */ + targetSSR?: boolean; /** A [minimatch pattern](https://github.com/isaacs/minimatch), or array of patterns, which specifies the files in the build the plugin should transform on. By default all files are targeted. */ include?: FilterPattern; /** A [minimatch pattern](https://github.com/isaacs/minimatch), or array of patterns, which specifies the files in the build the plugin should not transform. By default no files are ignored. */ @@ -149,6 +151,7 @@ export default function lwc(pluginOptions: RollupLwcOptions = {}): Plugin { let { rootDir, modules = [] } = pluginOptions; const { + targetSSR, stylesheetConfig, sourcemap = false, preserveHtmlComments, @@ -331,6 +334,7 @@ export default function lwc(pluginOptions: RollupLwcOptions = {}): Plugin { ...('enableStaticContentOptimization' in pluginOptions && { enableStaticContentOptimization: pluginOptions.enableStaticContentOptimization, }), + targetSSR, }); if (warnings) { diff --git a/packages/@lwc/ssr-compiler/jest.config.js b/packages/@lwc/ssr-compiler/jest.config.js new file mode 100644 index 0000000000..dab5bdbbf9 --- /dev/null +++ b/packages/@lwc/ssr-compiler/jest.config.js @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2023, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ +const BASE_CONFIG = require('../../../scripts/jest/base.config'); + +module.exports = { + ...BASE_CONFIG, + displayName: 'lwc-ssr-compiler', +}; diff --git a/packages/@lwc/ssr-compiler/package.json b/packages/@lwc/ssr-compiler/package.json new file mode 100644 index 0000000000..da15c66a64 --- /dev/null +++ b/packages/@lwc/ssr-compiler/package.json @@ -0,0 +1,59 @@ +{ + "//": [ + "THIS FILE IS AUTOGENERATED. If you modify it, it will be rewritten by check-and-rewrite-package-json.js", + "You can safely modify dependencies, devDependencies, keywords, etc., but other props will be overwritten." + ], + "name": "@lwc/ssr-compiler", + "version": "6.1.1", + "description": "Compile component for use during server-side rendering", + "keywords": [ + "lwc" + ], + "homepage": "https://lwc.dev", + "repository": { + "type": "git", + "url": "https://github.com/salesforce/lwc.git", + "directory": "packages/@lwc/ssr-compiler" + }, + "bugs": { + "url": "https://github.com/salesforce/lwc/issues" + }, + "license": "MIT", + "publishConfig": { + "access": "public" + }, + "main": "dist/index.cjs.js", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "rollup --config ../../../scripts/rollup/rollup.config.js", + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", + "test-manual": "jest ./src/**/*.spec.ts" + }, + "nx": { + "targets": { + "build": { + "outputs": [ + "{projectRoot}/dist" + ] + } + } + }, + "dependencies": { + "@lwc/metadata": "6.1.0-0", + "@lwc/sfdc-compiler-utils": "6.1.0-0", + "@lwc/style-compiler": "6.1.1", + "@lwc/template-compiler": "6.1.1", + "acorn": "~8.10.0", + "astring": "^1.8.6", + "estree-toolkit": "^1.7.3", + "immer": "^10.0.3", + "meriyah": "^4.3.8" + }, + "devDependencies": { + "@types/estree": "^1.0.5" + } +} diff --git a/packages/@lwc/ssr-compiler/src/__tests__/fixtures.spec.ts b/packages/@lwc/ssr-compiler/src/__tests__/fixtures.spec.ts new file mode 100644 index 0000000000..432e716b43 --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/__tests__/fixtures.spec.ts @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2020, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import fs from 'fs'; +import path from 'path'; + +import { rollup, RollupLog } from 'rollup'; +// @ts-ignore +import lwcRollupPlugin from '@lwc/rollup-plugin'; +import { isVoidElement, HTML_NAMESPACE } from '@lwc/shared'; +import { testFixtureDir } from '@lwc/jest-utils-lwc-internals'; +import { serverSideRenderComponent } from '../index'; + +interface FixtureModule { + tagName: string; + default: any; + generateMarkup: any; + props?: { [key: string]: any }; + features?: any[]; +} + +jest.setTimeout(10_000 /* 10 seconds */); + +async function compileFixture({ input, dirname }: { input: string; dirname: string }) { + const modulesDir = path.resolve(dirname, './modules'); + const outputFile = path.resolve(dirname, './dist/compiled-experimental-ssr.js'); + // TODO [#3331]: this is only needed to silence warnings on lwc:dynamic, remove in 246. + const warnings: RollupLog[] = []; + + const bundle = await rollup({ + input, + external: ['lwc'], + plugins: [ + lwcRollupPlugin({ + targetSSR: true, + enableDynamicComponents: true, + modules: [ + { + dir: modulesDir, + }, + ], + }), + ], + onwarn(warning) { + warnings.push(warning); + }, + }); + + await bundle.write({ + file: outputFile, + format: 'cjs', + exports: 'named', + }); + + return outputFile; +} + +/** + * Naive HTML fragment formatter. + * + * This is a replacement for Prettier HTML formatting. Prettier formatting is too aggressive for + * fixture testing. It not only indent the HTML code but also fixes HTML issues. For testing we want + * to make sure that the fixture file is as close as possible to what the engine produces. + */ +function formatHTML(src: string): string { + let res = ''; + let pos = 0; + let start = pos; + + let depth = 0; + + const getPadding = () => { + return ' '.repeat(depth); + }; + + while (pos < src.length) { + // Consume element tags and comments. + if (src.charAt(pos) === '<') { + const tagNameMatch = src.slice(pos).match(/(\w+)/); + + // Special handling for `' + '\n'; + start = pos = pos + wholeMatch.length; + continue; + } + } + + const isVoid = isVoidElement(tagNameMatch![0], HTML_NAMESPACE); + const isClosing = src.charAt(pos + 1) === '/'; + const isComment = + src.charAt(pos + 1) === '!' && + src.charAt(pos + 2) === '-' && + src.charAt(pos + 3) === '-'; + + start = pos; + while (src.charAt(pos++) !== '>') { + // Keep advancing until consuming the closing tag. + } + + // Adjust current depth and print the element tag or comment. + if (isClosing) { + depth--; + } + + res += getPadding() + src.slice(start, pos) + '\n'; + + const isSelfClosing = src.charAt(pos - 2) === '/'; + if (!isClosing && !isSelfClosing && !isVoid && !isComment) { + depth++; + } + } + + // Consume text content. + start = pos; + while (src.charAt(pos) !== '<' && pos < src.length) { + pos++; + } + + if (start !== pos) { + res += getPadding() + src.slice(start, pos) + '\n'; + } + } + + return res.trim(); +} + +function testFixtures() { + testFixtureDir( + { + root: path.resolve(__dirname, '../../../engine-server/src/__tests__/fixtures'), + pattern: '**/index.js', + }, + async ({ filename, dirname }) => { + const configPath = path.resolve(dirname, 'config.json'); + + let config: any = {}; + if (fs.existsSync(configPath)) { + config = require(configPath); + } + + const compiledFixturePath = await compileFixture({ + input: filename, + dirname, + }); + + let module: FixtureModule; + jest.isolateModules(() => { + module = require(compiledFixturePath); + }); + + try { + const result = await serverSideRenderComponent( + module!.tagName, + module!.generateMarkup, + config.props || {} + ); + return { + 'expected.html': formatHTML(result), + 'error.txt': undefined, + }; + } catch (_err: any) { + return { + 'error.txt': _err.message, + 'expected.html': undefined, + }; + } + } + ); +} + +describe('fixtures', () => { + testFixtures(); +}); diff --git a/packages/@lwc/ssr-compiler/src/compile-js/catalog-tmpls.ts b/packages/@lwc/ssr-compiler/src/compile-js/catalog-tmpls.ts new file mode 100644 index 0000000000..fd88feed3a --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/compile-js/catalog-tmpls.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import type { NodePath } from 'estree-toolkit'; +import type { ImportDeclaration } from 'estree'; +import type { ComponentMetaState } from './types'; + +export function catalogTmplImport(path: NodePath, state: ComponentMetaState) { + const specifier = path.node!.specifiers[0]; + + if ( + typeof path.node!.source.value !== 'string' || + !path.node!.source.value!.endsWith('.html') || + path.node!.specifiers.length !== 1 || + specifier.type !== 'ImportDefaultSpecifier' + ) { + return; + } + + state.tmplExplicitImports = state.tmplExplicitImports ?? new Map(); + state.tmplExplicitImports.set(specifier.local.name, path.node!.source.value); +} diff --git a/packages/@lwc/ssr-compiler/src/compile-js/generate-markup.ts b/packages/@lwc/ssr-compiler/src/compile-js/generate-markup.ts new file mode 100644 index 0000000000..a689cbb493 --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/compile-js/generate-markup.ts @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import { is, builders as b } from 'estree-toolkit'; +import { AriaPropNameToAttrNameMap } from '@lwc/shared'; +import { esTemplate } from '../estemplate'; +import { isIdentOrRenderCall, isNullableOf } from '../estree/validators'; +import { bImportDeclaration } from '../estree/builders'; + +import type { + ExportNamedDeclaration, + ExpressionStatement, + Program, + ImportDeclaration, + Property, +} from 'estree'; +import type { ComponentMetaState } from './types'; + +const isNullableExpressionStatement = isNullableOf(is.expressionStatement); + +const bGenerateMarkup = esTemplate` + export async function* generateMarkup(tagName, props, attrs, slotted) { + attrs = attrs ?? {}; + ${isNullableExpressionStatement}; + const instance = new ${is.identifier}({ + tagName: tagName.toUpperCase(), + }); + instance.__internal__setState(props, __REFLECTED_PROPS__, attrs); + instance.isConnected = true; + instance.connectedCallback?.(); + yield \`<\${tagName}\`; + yield *__renderAttrs(attrs) + yield '>'; + const tmplFn = ${isIdentOrRenderCall} ?? __fallbackTmpl; + yield* tmplFn(props, attrs, slotted, ${is.identifier}, instance, defaultStylesheets); + yield \`\`; + } +`; + +const bInsertFallbackTmplImport = esTemplate` + import { fallbackTmpl as __fallbackTmpl, renderAttrs as __renderAttrs } from '@lwc/ssr-runtime'; +`; + +const bCreateReflectedPropArr = esTemplate` + const __REFLECTED_PROPS__ = ${is.arrayExpression}; +`; + +function bReflectedAttrsObj(reflectedPropNames: (keyof typeof AriaPropNameToAttrNameMap)[]) { + // This will build getter properties for each reflected property. It'll look + // something like this: + // + // get ['aria-checked']() { + // return props.ariaChecked; + // } + // + // The props object will be kept up-to-date with any new values set on the corresponding + // property name in the component instance. + const reflectedAttrGetters: Property[] = reflectedPropNames.map((propName) => + b.property( + 'get', + b.literal(AriaPropNameToAttrNameMap[propName]), + b.functionExpression( + null, + [], + b.blockStatement([ + b.returnStatement( + b.memberExpression(b.identifier('props'), b.identifier(propName)) + ), + ]) + ) + ) + ); + + // attrs = { + // ...attrs, + // get ['aria-checked']() { + // return props.ariaChecked; + // } + // } + return b.expressionStatement( + b.assignmentExpression( + '=', + b.identifier('attrs'), + b.objectExpression([b.spreadElement(b.identifier('attrs')), ...reflectedAttrGetters]) + ) + ); +} + +export function addGenerateMarkupExport( + program: Program, + state: ComponentMetaState, + filename: string +) { + const { hasRenderMethod, tmplExplicitImports } = state; + + const classIdentifier = b.identifier(state.lwcClassName!); + const renderCall = hasRenderMethod + ? b.callExpression(b.memberExpression(b.identifier('instance'), b.identifier('render')), []) + : b.identifier('tmpl'); + + if (!tmplExplicitImports) { + const defaultTmplPath = filename.replace(/\.js$/, '.html'); + program.body.unshift(bImportDeclaration(b.identifier('tmpl'), b.literal(defaultTmplPath))); + } + + let attrsAugmentation: ExpressionStatement | null = null; + if (state.reflectedPropsInPlay.size) { + attrsAugmentation = bReflectedAttrsObj([...state.reflectedPropsInPlay]); + } + const reflectedPropArr = b.arrayExpression( + [...state.reflectedPropsInPlay].map((propName) => b.literal(propName)) + ); + + program.body.unshift(bInsertFallbackTmplImport()); + program.body.push(bCreateReflectedPropArr(reflectedPropArr)); + program.body.push( + bGenerateMarkup(attrsAugmentation, classIdentifier, renderCall, classIdentifier) + ); +} diff --git a/packages/@lwc/ssr-compiler/src/compile-js/index.ts b/packages/@lwc/ssr-compiler/src/compile-js/index.ts new file mode 100644 index 0000000000..11ffddcd67 --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/compile-js/index.ts @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import { generate } from 'astring'; +import { traverse, builders as b, is } from 'estree-toolkit'; +import { parseModule } from 'meriyah'; +import { AriaPropNameToAttrNameMap } from '@lwc/shared'; + +import { replaceLwcImport } from './lwc-import'; +import { catalogTmplImport } from './catalog-tmpls'; +import { addStylesheetImports, catalogStaticStylesheets, catalogStyleImport } from './stylesheets'; +import { addGenerateMarkupExport } from './generate-markup'; + +import type { Identifier as EsIdentifier, Program as EsProgram } from 'estree'; +import type { Visitors, ComponentMetaState } from './types'; + +const visitors: Visitors = { + $: { scope: true }, + Identifier(path, state) { + const reflectedAttrName = AriaPropNameToAttrNameMap[path.node!.name]; + if (reflectedAttrName) { + state.reflectedPropsInPlay.add(path.node!.name); + } + }, + ImportDeclaration(path, state) { + if (!path.node || !path.node.source.value || typeof path.node.source.value !== 'string') { + return; + } + + replaceLwcImport(path, state); + catalogTmplImport(path, state); + catalogStyleImport(path, state); + }, + ExportDefaultDeclaration(_path, _state) {}, + ClassDeclaration(path, state) { + if ( + path.node?.superClass?.type === 'Identifier' && + // It is possible to inherit from something that inherits from + // LightningElement, so the detection here needs additional work. + path.node?.superClass?.name === 'LightningElement' + ) { + state.isLWC = true; + if (path.node!.id) { + state.lwcClassName = path.node!.id.name; + } else { + path.node!.id = b.identifier('DefaultComponentName'); + state.lwcClassName = 'DefaultComponentName'; + } + } + }, + PropertyDefinition(path, state) { + if ( + path.node && + path.node.static && + is.identifier(path.node.key) && + path.node.key.name === 'stylesheets' && + is.arrayExpression(path.node.value) && + path.node.value.elements.every((el) => is.identifier(el)) + ) { + catalogStaticStylesheets( + path.node.value.elements.map((el) => (el as EsIdentifier).name), + state + ); + } + }, + MethodDefinition(path, state) { + if (path.node?.key.type !== 'Identifier') { + return; + } + switch (path.node.key.name) { + case 'constructor': + path.node.value.params = [b.identifier('propsAvailableAtConstruction')]; + break; + case 'connectedCallback': + state.hasConnectedCallback = true; + break; + case 'render': + state.hasRenderMethod = true; + break; + case 'renderedCallback': + state.hadRenderedCallback = true; + path.remove(); + break; + case 'disconnectedCallback': + state.hadDisconnectedCallback = true; + path.remove(); + break; + case 'errorCallback': + state.hadErrorCallback = true; + path.remove(); + break; + } + }, + Super(path, _state) { + const parentFn = path.getFunctionParent(); + if ( + parentFn && + parentFn.parentPath?.node?.type === 'MethodDefinition' && + parentFn.parentPath?.node?.kind === 'constructor' && + path.parentPath && + path.parentPath.node?.type === 'CallExpression' + ) { + path.parentPath.node.arguments = [b.identifier('propsAvailableAtConstruction')]; + } + }, +}; + +export default function compileJS(src: string, filename: string) { + const ast = parseModule(src, { + module: true, + next: true, + }) as EsProgram; + + const state: ComponentMetaState = { + isLWC: false, + hasConstructor: false, + hasConnectedCallback: false, + hasRenderMethod: false, + hadRenderedCallback: false, + hadDisconnectedCallback: false, + hadErrorCallback: false, + lightningElementIdentifier: null, + lwcClassName: null, + tmplExplicitImports: null, + cssExplicitImports: null, + staticStylesheetIds: null, + props: [], + reflectedPropsInPlay: new Set(), + }; + + traverse(ast, visitors, state); + + if (!state.isLWC) { + // If an `extends LightningElement` is not detected in the JS, the + // file in question is likely not an LWC. With this v1 implementation, + // we'll just return the original source. + return { + code: src, + }; + } + + addGenerateMarkupExport(ast, state, filename); + addStylesheetImports(ast, state, filename); + + return { + code: generate(ast, {}), + }; +} diff --git a/packages/@lwc/ssr-compiler/src/compile-js/lwc-import.ts b/packages/@lwc/ssr-compiler/src/compile-js/lwc-import.ts new file mode 100644 index 0000000000..cb2c92818a --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/compile-js/lwc-import.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import { builders as b } from 'estree-toolkit'; + +import type { ImportDeclaration } from 'estree'; +import type { NodePath } from 'estree-toolkit'; +import type { ComponentMetaState } from './types'; + +export function replaceLwcImport(path: NodePath, state: ComponentMetaState) { + if (path.node!.source.value !== 'lwc') { + return; + } + + const node = path.node!; + for (const specifier of node.specifiers) { + if ( + specifier.type === 'ImportSpecifier' && + specifier.imported.type === 'Identifier' && + specifier.imported.name === 'LightningElement' + ) { + state.lightningElementIdentifier = specifier.local.name; + break; + } + } + + path.replaceWith(b.importDeclaration(node.specifiers, b.literal('@lwc/ssr-runtime'))); +} diff --git a/packages/@lwc/ssr-compiler/src/compile-js/stylesheets.ts b/packages/@lwc/ssr-compiler/src/compile-js/stylesheets.ts new file mode 100644 index 0000000000..f7649ea88d --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/compile-js/stylesheets.ts @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import { builders as b, is } from 'estree-toolkit'; +import { esTemplate } from '../estemplate'; + +import type { NodePath } from 'estree-toolkit'; +import type { Program, ImportDeclaration } from 'estree'; +import type { ComponentMetaState } from './types'; + +const bDefaultStyleImport = esTemplate` + import defaultStylesheets from '${is.literal}'; +`; + +export function catalogStyleImport(path: NodePath, state: ComponentMetaState) { + const specifier = path.node!.specifiers[0]; + + if ( + typeof path.node!.source.value !== 'string' || + !path.node!.source.value!.endsWith('.css') || + path.node!.specifiers.length !== 1 || + specifier.type !== 'ImportDefaultSpecifier' + ) { + return; + } + + state.cssExplicitImports = state.cssExplicitImports ?? new Map(); + state.cssExplicitImports.set(specifier.local.name, path.node!.source.value); +} + +const componentNamePattern = /\/(?[a-z_-]+)\/\k\.[tj]s$/; + +export function addStylesheetImports(ast: Program, state: ComponentMetaState, filepath: string) { + const componentName = componentNamePattern.exec(filepath)?.groups?.componentName; + if (!componentName) { + throw new Error(`Could not determine component name from file path: ${filepath}`); + } + + if (state.cssExplicitImports || state.staticStylesheetIds) { + throw new Error( + `Unimplemented static stylesheets, but found:\n${[...state.cssExplicitImports!].join( + ' \n' + )}` + ); + } + + ast.body.unshift(bDefaultStyleImport(b.literal(`./${componentName}.css`))); +} + +export function catalogStaticStylesheets(ids: string[], state: ComponentMetaState) { + state.staticStylesheetIds = state.staticStylesheetIds ?? new Set(); + for (const id of ids) { + state.staticStylesheetIds.add(id); + } +} diff --git a/packages/@lwc/ssr-compiler/src/compile-js/types.ts b/packages/@lwc/ssr-compiler/src/compile-js/types.ts new file mode 100644 index 0000000000..0ba0109fd6 --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/compile-js/types.ts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import type { traverse } from 'estree-toolkit'; +import type { Node } from 'estree'; + +export type Visitors = Parameters>[1]; + +export interface ComponentMetaState { + isLWC: boolean; + hasConstructor: boolean; + hasConnectedCallback: boolean; + hasRenderMethod: boolean; + hadRenderedCallback: boolean; + hadDisconnectedCallback: boolean; + hadErrorCallback: boolean; + lightningElementIdentifier: string | null; + lwcClassName: string | null; + tmplExplicitImports: Map | null; + cssExplicitImports: Map | null; + staticStylesheetIds: Set | null; + props: string[]; + reflectedPropsInPlay: Set; +} diff --git a/packages/@lwc/ssr-compiler/src/compile-template/comment.ts b/packages/@lwc/ssr-compiler/src/compile-template/comment.ts new file mode 100644 index 0000000000..57f3a014bb --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/compile-template/comment.ts @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import { builders as b } from 'estree-toolkit'; + +import type { Comment as IrComment } from '@lwc/template-compiler'; +import type { Transformer } from './types'; + +export const Comment: Transformer = function Comment(node, cxt) { + if (cxt.templateOptions.preserveComments) { + return [b.expressionStatement(b.yieldExpression(b.literal(``)))]; + } else { + return []; + } +}; diff --git a/packages/@lwc/ssr-compiler/src/compile-template/component.ts b/packages/@lwc/ssr-compiler/src/compile-template/component.ts new file mode 100644 index 0000000000..7f859c399c --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/compile-template/component.ts @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import { builders as b, is } from 'estree-toolkit'; +import { kebabcaseToCamelcase, toPropertyName } from '@lwc/template-compiler'; +import { esTemplateWithYield } from '../estemplate'; +import { cleanStyleAttrVal, isValidIdentifier } from './shared'; +import { TransformerContext } from './types'; +import { expressionIrToEs } from './expression'; + +import type { + BlockStatement as EsBlockStatement, + ObjectExpression as EsObjectExpression, +} from 'estree'; +import type { + Attribute as IrAttribute, + Component as IrComponent, + Property as IrProperty, +} from '@lwc/template-compiler'; +import type { Transformer } from './types'; + +const bYieldFromChildGenerator = esTemplateWithYield` + { + const childProps = ${is.objectExpression}; + const childAttrs = ${is.objectExpression}; + const childSlottedContentGenerators = {}; + yield* ${is.identifier}(${is.literal}, childProps, childAttrs, childSlottedContentGenerators); + } +`; + +const bImportGenerateMarkup = (localName: string, importPath: string) => + b.importDeclaration( + [b.importSpecifier(b.identifier('generateMarkup'), b.identifier(localName))], + b.literal(importPath) + ); + +function getChildAttrsOrProps( + attrs: (IrAttribute | IrProperty)[], + cxt: TransformerContext +): EsObjectExpression { + const objectAttrsOrProps = attrs.map((attr) => { + const key = isValidIdentifier(attr.name) ? b.identifier(attr.name) : b.literal(attr.name); + if (attr.value.type === 'Literal' && typeof attr.value.value === 'string') { + const value = + attr.name === 'style' ? cleanStyleAttrVal(attr.value.value) : attr.value.value; + return b.property('init', key, b.literal(value)); + } else if (attr.value.type === 'Literal' && typeof attr.value.value === 'boolean') { + return b.property('init', key, b.literal(attr.value.value)); + } else if (attr.value.type === 'Identifier' || attr.value.type === 'MemberExpression') { + const propValue = expressionIrToEs(attr.value, cxt); + return b.property('init', key, propValue); + } + throw new Error(`Unimplemented child attr IR node type: ${attr.value.type}`); + }); + + return b.objectExpression(objectAttrsOrProps); +} + +function reflectAriaPropsAsAttrs(props: IrProperty[]): IrAttribute[] { + return props + .map((prop) => { + if (prop.attributeName.startsWith('aria-') || prop.attributeName === 'role') { + return { + type: 'Attribute', + name: prop.attributeName, + value: prop.value, + } as IrAttribute; + } + return null; + }) + .filter((el): el is NonNullable => el !== null); +} + +export const Component: Transformer = function Component(node, cxt) { + // Import the custom component's generateMarkup export. + const childGeneratorLocalName = `generateMarkup_${toPropertyName(node.name)}`; + const importPath = kebabcaseToCamelcase(node.name); + const componentImport = bImportGenerateMarkup(childGeneratorLocalName, importPath); + cxt.hoist(componentImport, childGeneratorLocalName); + const childTagName = node.name; + + const attributes = [...node.attributes, ...reflectAriaPropsAsAttrs(node.properties)]; + + return [ + bYieldFromChildGenerator( + getChildAttrsOrProps(node.properties, cxt), + getChildAttrsOrProps(attributes, cxt), + b.identifier(childGeneratorLocalName), + b.literal(childTagName) + ), + ]; +}; diff --git a/packages/@lwc/ssr-compiler/src/compile-template/context.ts b/packages/@lwc/ssr-compiler/src/compile-template/context.ts new file mode 100644 index 0000000000..1d1c51a103 --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/compile-template/context.ts @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import type { ModuleDeclaration as EsModuleDeclaration, Statement as EsStatement } from 'estree'; +import type { TemplateOpts, TransformerContext } from './types'; + +function* reversed(arr: T[]): Generator { + for (let idx = arr.length - 1; idx > -1; idx--) { + yield arr[idx]; + } +} + +const identifierChars = 'abcdefghijklmnopqrstuvwxyz'; + +function genId(n: number, prevChars = ''): string { + const remaining = Math.floor(n / identifierChars.length); + const result = identifierChars.charAt(n % identifierChars.length) + prevChars; + + return remaining <= 0 ? result : genId(remaining - 1, result); +} + +function* genIds() { + let counter = 0; + while (true) { + yield genId(counter++); + } +} + +export interface ContextOpts { + preserveComments: boolean; +} + +export function createNewContext(templateOptions: TemplateOpts): { + hoisted: Map; + cxt: TransformerContext; +} { + const hoisted = new Map(); + const hoist = (stmt: EsStatement | EsModuleDeclaration, dedupeKey: string) => + hoisted.set(dedupeKey, stmt); + + const localVarStack: Set[] = []; + + const pushLocalVars = (vars: string[]) => { + localVarStack.push(new Set(vars)); + }; + const popLocalVars = () => { + localVarStack.pop(); + }; + const isLocalVar = (varName: string | null | undefined) => { + if (!varName) { + return false; + } + for (const stackFrame of reversed(localVarStack)) { + if (stackFrame.has(varName)) { + return true; + } + } + return false; + }; + + const uniqueVarGenerator = genIds(); + const getUniqueVar = () => uniqueVarGenerator.next().value!; + + return { + hoisted, + cxt: { + hoist, + pushLocalVars, + popLocalVars, + isLocalVar, + getUniqueVar, + templateOptions, + }, + }; +} diff --git a/packages/@lwc/ssr-compiler/src/compile-template/element.ts b/packages/@lwc/ssr-compiler/src/compile-template/element.ts new file mode 100644 index 0000000000..30bf95a08d --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/compile-template/element.ts @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import { builders as b, is } from 'estree-toolkit'; +import { HTML_NAMESPACE, isVoidElement } from '@lwc/shared'; +import { esTemplateWithYield } from '../estemplate'; +import { irToEs } from './ir-to-es'; +import { bImportHtmlEscape, importHtmlEscapeKey, cleanStyleAttrVal } from './shared'; + +import type { + Attribute as IrAttribute, + Expression as IrExpression, + Element as IrElement, + Literal as IrLiteral, + Property as IrProperty, +} from '@lwc/template-compiler'; +import type { + BlockStatement as EsBlockStatement, + Expression as EsExpression, + Statement as EsStatement, +} from 'estree'; +import type { Transformer } from './types'; + +const bYield = (expr: EsExpression) => b.expressionStatement(b.yieldExpression(expr)); +const bConditionalLiveYield = esTemplateWithYield` + { + const attrOrPropValue = ${is.expression}; + const valueType = typeof attrOrPropValue; + if (attrOrPropValue && (valueType === 'string' || valueType === 'boolean')) { + yield ' ' + ${is.literal}; + if (valueType === 'string') { + yield '="' + htmlEscape(attrOrPropValue, true) + '"'; + } + } + } +`; + +function yieldAttrOrPropLiteralValue(name: string, valueNode: IrLiteral): EsStatement[] { + const { value, type } = valueNode; + if (typeof value === 'string') { + const yieldedValue = name === 'style' ? cleanStyleAttrVal(value) : value; + return [bYield(b.literal(` ${name}="${yieldedValue}"`))]; + } else if (typeof value === 'boolean') { + return [bYield(b.literal(` ${name}`))]; + } + throw new Error(`Unknown attr/prop literal: ${type}`); +} + +function yieldAttrOrPropLiveValue(name: string, value: IrExpression): EsStatement[] { + const instanceMemberRef = b.memberExpression(b.identifier('instance'), value as EsExpression); + return [bConditionalLiveYield(instanceMemberRef, b.literal(name))]; +} + +function reorderAttributes(attrs: (IrAttribute | IrProperty)[]): (IrAttribute | IrProperty)[] { + let classAttr: IrAttribute | IrProperty | null = null; + let styleAttr: IrAttribute | IrProperty | null = null; + + const boringAttrs = attrs.filter((attr) => { + if (attr.name === 'class') { + classAttr = attr; + return false; + } else if (attr.name === 'style') { + styleAttr = attr; + return false; + } + return true; + }); + + return [classAttr, styleAttr, ...boringAttrs].filter( + (el): el is NonNullable => el !== null + ); +} + +export const Element: Transformer = function Element(node, cxt): EsStatement[] { + const attrsAndProps: (IrAttribute | IrProperty)[] = reorderAttributes([ + ...node.attributes, + ...node.properties, + ]); + + const yieldAttrsAndProps = attrsAndProps + .map((attr) => { + cxt.hoist(bImportHtmlEscape(), importHtmlEscapeKey); + if (attr.value.type === 'Literal') { + return yieldAttrOrPropLiteralValue(attr.name, attr.value); + } else { + return yieldAttrOrPropLiveValue(attr.name, attr.value); + } + }) + .flat(); + + if (isVoidElement(node.name, HTML_NAMESPACE)) { + return [bYield(b.literal(`<${node.name}`)), ...yieldAttrsAndProps, bYield(b.literal(`>`))]; + } + + return [ + bYield(b.literal(`<${node.name}`)), + ...yieldAttrsAndProps, + bYield(b.literal(`>`)), + ...node.children.map((child) => irToEs(child, cxt)).flat(), + bYield(b.literal(``)), + ].filter(Boolean); +}; diff --git a/packages/@lwc/ssr-compiler/src/compile-template/expression.ts b/packages/@lwc/ssr-compiler/src/compile-template/expression.ts new file mode 100644 index 0000000000..63a3516a05 --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/compile-template/expression.ts @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import { builders as b } from 'estree-toolkit'; + +import type { + ComplexExpression as IrComplexExpression, + Expression as IrExpression, + Identifier as IrIdentifier, + MemberExpression as IrMemberExpression, +} from '@lwc/template-compiler'; +import type { + Identifier as EsIdentifier, + Expression as EsExpression, + MemberExpression as EsMemberExpression, +} from 'estree'; +import type { TransformerContext } from './types'; + +function getRootMemberExpression(node: IrMemberExpression): IrMemberExpression { + return node.object.type === 'MemberExpression' ? getRootMemberExpression(node.object) : node; +} + +export function expressionIrToEs( + node: IrExpression | IrComplexExpression, + cxt: TransformerContext +): EsExpression { + if (node.type === 'Identifier') { + const isLocalVar = cxt.isLocalVar((node as IrIdentifier).name); + return isLocalVar + ? (node as EsIdentifier) + : b.memberExpression(b.identifier('instance'), node as EsIdentifier); + } else if (node.type === 'MemberExpression') { + const nodeClone = structuredClone(node); + const rootMemberExpr = getRootMemberExpression(nodeClone as IrMemberExpression); + if (!cxt.isLocalVar((rootMemberExpr.object as IrIdentifier).name)) { + rootMemberExpr.object = b.memberExpression( + b.identifier('instance'), + rootMemberExpr.object as EsIdentifier + ) as unknown as IrMemberExpression; + } + return nodeClone as unknown as EsMemberExpression; + } + throw new Error(`Unimplemented expression: ${node.type}`); +} diff --git a/packages/@lwc/ssr-compiler/src/compile-template/for-each.ts b/packages/@lwc/ssr-compiler/src/compile-template/for-each.ts new file mode 100644 index 0000000000..2e2f785ca2 --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/compile-template/for-each.ts @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import { builders as b, is } from 'estree-toolkit'; +import { esTemplate } from '../estemplate'; +import { irToEs } from './ir-to-es'; +import { optimizeAdjacentYieldStmts } from './shared'; + +import type { ForEach as IrForEach } from '@lwc/template-compiler'; +import type { + Expression as EsExpression, + ForOfStatement as EsForOfStatement, + Identifier as EsIdentifier, + Statement as EsStatement, + MemberExpression as EsMemberExpression, +} from 'estree'; +import type { Transformer } from './types'; + +function getRootMemberExpression(node: EsMemberExpression): EsMemberExpression { + return node.object.type === 'MemberExpression' ? getRootMemberExpression(node.object) : node; +} + +function getRootIdentifier(node: EsMemberExpression): EsIdentifier | null { + const rootMemberExpression = getRootMemberExpression(node); + return is.identifier(rootMemberExpression?.object) ? rootMemberExpression.object : null; +} + +const bForOfYieldFrom = esTemplate< + EsForOfStatement, + [EsIdentifier, EsIdentifier, EsExpression, EsStatement[]] +>` + for (let [${is.identifier}, ${is.identifier}] of Object.entries(${is.expression} ?? {})) { + ${is.statement}; + } +`; + +export const ForEach: Transformer = function ForEach(node, cxt): EsForOfStatement[] { + const forItemId = node.item.name; + const forIndexId = node.index?.name ?? '__unused__'; + + cxt.pushLocalVars([forItemId, forIndexId]); + const forEachStatements = node.children.flatMap((childNode) => { + return irToEs(childNode, cxt); + }); + cxt.popLocalVars(); + + const expression = node.expression as EsExpression; + + const scopeReferencedId = is.memberExpression(expression) + ? getRootIdentifier(expression) + : null; + const iterable = cxt.isLocalVar(scopeReferencedId?.name) + ? (node.expression as EsExpression) + : b.memberExpression(b.identifier('instance'), node.expression as EsExpression); + + return [ + bForOfYieldFrom( + b.identifier(forIndexId), + b.identifier(forItemId), + iterable, + optimizeAdjacentYieldStmts(forEachStatements) + ), + ]; +}; diff --git a/packages/@lwc/ssr-compiler/src/compile-template/if.ts b/packages/@lwc/ssr-compiler/src/compile-template/if.ts new file mode 100644 index 0000000000..7bfb200f3b --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/compile-template/if.ts @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import { builders as b } from 'estree-toolkit'; +import { irToEs } from './ir-to-es'; +import { expressionIrToEs } from './expression'; +import { optimizeAdjacentYieldStmts } from './shared'; + +import type { + ChildNode as IrChildNode, + ElseifBlock as IrElseifBlock, + If as IrIf, + IfBlock as IrIfBlock, +} from '@lwc/template-compiler'; +import type { BlockStatement as EsBlockStatement, IfStatement as EsIfStatement } from 'estree'; +import type { Transformer, TransformerContext } from './types'; + +function bBlockStatement(childNodes: IrChildNode[], cxt: TransformerContext): EsBlockStatement { + return b.blockStatement( + optimizeAdjacentYieldStmts(childNodes.flatMap((childNode) => irToEs(childNode, cxt))) + ); +} + +export const If: Transformer = function If(node, cxt) { + const { modifier: trueOrFalseAsStr, condition, children } = node; + + const trueOrFalse = trueOrFalseAsStr === 'true'; + const comparison = b.binaryExpression( + '===', + b.literal(trueOrFalse), + expressionIrToEs(condition, cxt) + ); + + return [b.ifStatement(comparison, bBlockStatement(children, cxt))]; +}; + +function bIfStatement( + ifElseIfNode: IrIfBlock | IrElseifBlock, + cxt: TransformerContext +): EsIfStatement { + const { children, condition, else: elseNode } = ifElseIfNode; + + let elseBlock = null; + if (elseNode) { + if (elseNode.type === 'ElseBlock') { + elseBlock = bBlockStatement(elseNode.children, cxt); + } else { + elseBlock = bIfStatement(elseNode, cxt); + } + } + + return b.ifStatement( + expressionIrToEs(condition, cxt), + bBlockStatement(children, cxt), + elseBlock + ); +} + +export const IfBlock: Transformer = function IfBlock(node, cxt) { + return [bIfStatement(node, cxt)]; +}; diff --git a/packages/@lwc/ssr-compiler/src/compile-template/index.ts b/packages/@lwc/ssr-compiler/src/compile-template/index.ts new file mode 100644 index 0000000000..4920820ed8 --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/compile-template/index.ts @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import { generate } from 'astring'; +import { is, builders as b } from 'estree-toolkit'; +import { parse } from '@lwc/template-compiler'; +import { esTemplate } from '../estemplate'; +import { templateIrToEsTree } from './ir-to-es'; +import { optimizeAdjacentYieldStmts } from './shared'; + +import type { + Node as EsNode, + Statement as EsStatement, + Literal as EsLiteral, + ExportDefaultDeclaration as EsExportDefaultDeclaration, +} from 'estree'; + +const isBool = (node: EsNode | null) => is.literal(node) && typeof node.value === 'boolean'; + +const bExportTemplate = esTemplate< + EsExportDefaultDeclaration, + [EsLiteral, EsStatement[], EsLiteral] +>` + export default async function* tmpl(props, attrs, slotted, Cmp, instance, stylesheets) { + if (!${isBool} && Cmp.renderMode !== 'light') { + yield \`' + } + } +`; + +export default function compileTemplate(src: string, _filename: string) { + const { root, warnings } = parse(src); + if (!root || warnings.length) { + for (const warning of warnings) { + // eslint-disable-next-line no-console + console.error('Cannot compile:', warning.message); + } + throw new Error('Template compilation failure; see warnings in the console.'); + } + + const tmplRenderMode = + root!.directives.find((directive) => directive.name === 'RenderMode')?.value?.value ?? + 'shadow'; + const astShadowModeBool = tmplRenderMode === 'light' ? b.literal(true) : b.literal(false); + + const preserveComments = !!root!.directives.find( + (directive) => directive.name === 'PreserveComments' + )?.value?.value; + + const { hoisted, statements } = templateIrToEsTree(root!, { preserveComments }); + + const moduleBody = [ + ...hoisted, + bExportTemplate( + astShadowModeBool, + optimizeAdjacentYieldStmts(statements), + astShadowModeBool + ), + ]; + const program = b.program(moduleBody, 'module'); + + return { + code: generate(program, {}), + }; +} diff --git a/packages/@lwc/ssr-compiler/src/compile-template/ir-to-es.ts b/packages/@lwc/ssr-compiler/src/compile-template/ir-to-es.ts new file mode 100644 index 0000000000..b1906188db --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/compile-template/ir-to-es.ts @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import { inspect } from 'util'; + +import { Comment } from './comment'; +import { Component } from './component'; +import { Element } from './element'; +import { ForEach } from './for-each'; +import { If, IfBlock } from './if'; +import { Text } from './text'; +import { createNewContext } from './context'; + +import type { Node as IrNode, Root as IrRoot } from '@lwc/template-compiler'; +import type { Statement as EsStatement } from 'estree'; +import type { TemplateOpts, Transformer, TransformerContext } from './types'; + +const Root: Transformer = function Root(node, cxt): EsStatement[] { + return node.children.map((child) => irToEs(child, cxt)).flat(); +}; + +const transformers: Record = { + Comment: Comment as Transformer, + Component: Component as Transformer, + Root: Root as Transformer, + Text: Text as Transformer, + Element: Element as Transformer, + ForEach: ForEach as Transformer, + If: If as Transformer, + IfBlock: IfBlock as Transformer, +}; + +const defaultTransformer: Transformer = (node: IrNode) => { + throw new Error(`Unimplemented IR node: ${inspect(node)}`); +}; + +export function irToEs(node: IrNode, cxt: TransformerContext): EsStatement[] { + const transformer = transformers[node.type] ?? defaultTransformer; + return transformer(node, cxt); +} + +export function templateIrToEsTree(node: IrNode, contextOpts: TemplateOpts) { + const { hoisted, cxt } = createNewContext(contextOpts); + const statements = irToEs(node, cxt); + return { + hoisted: hoisted.values(), + statements, + }; +} diff --git a/packages/@lwc/ssr-compiler/src/compile-template/shared.ts b/packages/@lwc/ssr-compiler/src/compile-template/shared.ts new file mode 100644 index 0000000000..7bd7574d1d --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/compile-template/shared.ts @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import { is } from 'estree-toolkit'; +import { esTemplate } from '../estemplate'; + +import type { ImportDeclaration as EsImportDeclaration, Statement as EsStatement } from 'estree'; + +export const bImportHtmlEscape = esTemplate` + import { htmlEscape } from '@lwc/shared'; +`; +export const importHtmlEscapeKey = 'import:htmlEscape'; + +export function cleanStyleAttrVal(styleAttrVal: string): string { + if (styleAttrVal.endsWith(';')) { + styleAttrVal = styleAttrVal.slice(0, -1); + } + return styleAttrVal.trim(); +} + +const reservedKeywords = new Set([ + 'NaN', + 'arguments', + 'break', + 'case', + 'catch', + 'class', + 'const', + 'continue', + 'debugger', + 'default', + 'delete', + 'do', + 'else', + 'enum', + 'eval', + 'export', + 'extends', + 'false', + 'finally', + 'for', + 'function', + 'if', + 'implements', + 'import', + 'in', + 'instanceof', + 'interface', + 'let', + 'new', + 'null', + 'package', + 'private', + 'protected', + 'public', + 'return', + 'static', + 'super', + 'switch', + 'this', + 'throw', + 'true', + 'try', + 'typeof', + 'undefined', + 'var', + 'void', + 'while', + 'with', + 'yield', +]); + +const imperfectIdentifierMatcher = /^[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*$/; + +export const isValidIdentifier = (str: string) => + !reservedKeywords.has(str) && imperfectIdentifierMatcher.test(str); + +export function optimizeAdjacentYieldStmts(statements: EsStatement[]): EsStatement[] { + let prevStmt: EsStatement | null = null; + return statements + .map((stmt) => { + if ( + // Check if the current statement and previous statement are + // both yield expression statements that yield a string literal. + prevStmt && + is.expressionStatement(prevStmt) && + is.yieldExpression(prevStmt.expression) && + !prevStmt.expression.delegate && + prevStmt.expression.argument && + is.literal(prevStmt.expression.argument) && + typeof prevStmt.expression.argument.value === 'string' && + is.expressionStatement(stmt) && + is.yieldExpression(stmt.expression) && + !stmt.expression.delegate && + stmt.expression.argument && + is.literal(stmt.expression.argument) && + typeof stmt.expression.argument.value === 'string' + ) { + prevStmt.expression.argument.value += stmt.expression.argument.value; + return null; + } + prevStmt = stmt; + return stmt; + }) + .filter((el): el is NonNullable => el !== null); +} diff --git a/packages/@lwc/ssr-compiler/src/compile-template/text.ts b/packages/@lwc/ssr-compiler/src/compile-template/text.ts new file mode 100644 index 0000000000..730eed60bc --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/compile-template/text.ts @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import { builders as b, is } from 'estree-toolkit'; +import { esTemplateWithYield } from '../estemplate'; +import { bImportHtmlEscape, importHtmlEscapeKey } from './shared'; +import { expressionIrToEs } from './expression'; + +import type { + Expression as EsExpression, + Identifier as EsIdentifier, + Statement as EsStatement, +} from 'estree'; +import type { + ComplexExpression as IrComplexExpression, + Expression as IrExpression, + Literal as IrLiteral, + Text as IrText, +} from '@lwc/template-compiler'; +import type { Transformer } from './types'; + +const bYield = (expr: EsExpression) => b.expressionStatement(b.yieldExpression(expr)); + +const bYieldEscapedString = esTemplateWithYield< + EsStatement[], + [EsIdentifier, EsExpression, EsIdentifier, EsIdentifier, EsIdentifier, EsIdentifier] +>` + const ${is.identifier} = ${is.expression}; + yield ${is.identifier} === '' + ? '\\u200D' + : htmlEscape( + typeof ${is.identifier} === 'string' + ? ${is.identifier} + : (${is.identifier} ?? '__UNEXPECTED_NULLISH_TEXT_CONTENT__').toString() + ); +`; + +function isLiteral(node: IrLiteral | IrExpression | IrComplexExpression): node is IrLiteral { + return node.type === 'Literal'; +} + +export const Text: Transformer = function Text(node, cxt): EsStatement[] { + if (isLiteral(node.value)) { + return [bYield(b.literal(node.value.value))]; + } + + const valueToYield = expressionIrToEs(node.value, cxt); + cxt.hoist(bImportHtmlEscape(), importHtmlEscapeKey); + + const tempVariable = b.identifier(cxt.getUniqueVar()); + return bYieldEscapedString( + tempVariable, + valueToYield, + tempVariable, + tempVariable, + tempVariable, + tempVariable + ); +}; diff --git a/packages/@lwc/ssr-compiler/src/compile-template/types.ts b/packages/@lwc/ssr-compiler/src/compile-template/types.ts new file mode 100644 index 0000000000..bb93489f03 --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/compile-template/types.ts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import { Node as IrNode } from '@lwc/template-compiler'; +import type { ModuleDeclaration as EsModuleDeclaration, Statement as EsStatement } from 'estree'; + +export type Transformer = ( + node: T, + cxt: TransformerContext +) => EsStatement[]; + +export interface TransformerContext { + hoist: (stmt: EsStatement | EsModuleDeclaration, dedupeKey: string) => void; + pushLocalVars: (vars: string[]) => void; + popLocalVars: () => void; + isLocalVar: (varName: string | null | undefined) => boolean; + getUniqueVar: () => string; + templateOptions: TemplateOpts; +} + +export interface TemplateOpts { + preserveComments: boolean; +} diff --git a/packages/@lwc/ssr-compiler/src/estemplate.ts b/packages/@lwc/ssr-compiler/src/estemplate.ts new file mode 100644 index 0000000000..a545197705 --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/estemplate.ts @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import { traverse, Visitors } from 'estree-toolkit'; +import { parse } from 'acorn'; +import { produce } from 'immer'; +import type { Node, Program, FunctionDeclaration } from 'estree'; + +export const placeholder = false; + +type ReturnsBool = (node: Node | null) => boolean; +export type Validator = ReturnsBool | typeof placeholder; + +const PLACEHOLDER_PREFIX = `__ESTEMPLATE_${Math.random().toString().slice(2)}_PLACEHOLDER__`; + +interface TraversalState { + placeholderToValidator: Map; + replacementNodes: (Node | Node[] | null)[]; +} + +const visitors: Visitors = { + Identifier(path, state) { + if (path.node?.name.startsWith(PLACEHOLDER_PREFIX)) { + const key = path.node.name.slice(PLACEHOLDER_PREFIX.length); + const validateReplacement = state.placeholderToValidator.get(key)!; + const replacementNode = state.replacementNodes[key as unknown as number]; + + if ( + validateReplacement && + !(Array.isArray(replacementNode) + ? replacementNode.every(validateReplacement) + : validateReplacement(replacementNode)) + ) { + throw new Error(`Validation failed for templated node of type ${path.node.type}`); + } + + if (replacementNode === null) { + path.remove(); + } else if (Array.isArray(replacementNode)) { + if (replacementNode.length === 0) { + path.remove(); + } else { + if (path.parentPath!.node!.type === 'ExpressionStatement') { + path.parentPath!.replaceWithMultiple(replacementNode); + } else { + path.replaceWithMultiple(replacementNode); + } + } + } else { + path.replaceWith(replacementNode); + } + } + }, + Literal(path, state) { + if ( + typeof path.node?.value === 'string' && + path.node.value.startsWith(PLACEHOLDER_PREFIX) + ) { + const key = path.node.value.slice(PLACEHOLDER_PREFIX.length); + const validateReplacement = state.placeholderToValidator.get(key)!; + const replacementNode = state.replacementNodes[key as unknown as number] as Node; + + if (validateReplacement && !validateReplacement(replacementNode)) { + throw new Error(`Validation failed for templated node of type ${path.node.type}`); + } + + path.replaceWith(replacementNode); + } + }, +}; + +function esTemplateImpl< + ReturnType = Node, + ArgTypes extends (Node | Node[] | null)[] = (Node | Node[] | null)[] +>( + javascriptSegments: TemplateStringsArray, + validatorFns: Validator[], + wrap?: (code: string) => string, + unwrap?: (node: ReturnType) => any +): (...replacementNodes: ArgTypes) => ReturnType { + let placeholderCount = 0; + let parsableCode = javascriptSegments[0]; + validatorFns.reverse(); + const placeholderToValidator = new Map(); + + for (const segment of javascriptSegments.slice(1)) { + const validatorFn = validatorFns.pop(); + if (validatorFn) { + placeholderToValidator.set(placeholderCount.toString(), validatorFn); + } + parsableCode += `${PLACEHOLDER_PREFIX}${placeholderCount++}`; + parsableCode += segment; + } + + if (wrap) { + parsableCode = wrap(parsableCode); + } + + let originalAst = parse(parsableCode, { + ecmaVersion: 2022, + allowAwaitOutsideFunction: true, + allowReturnOutsideFunction: true, + allowSuperOutsideMethod: true, + allowImportExportEverywhere: true, + locations: false, + }) as Node; + + const originalAstProgram = originalAst as Program; + const finalCharacter = javascriptSegments.at(-1)?.trimEnd()?.at(-1); + if ( + originalAstProgram.body.length === 1 && + originalAstProgram.body[0].type === 'ExpressionStatement' && + finalCharacter !== ';' + ) { + originalAst = originalAstProgram.body[0].expression; + } else { + originalAst = originalAstProgram.body[0]; + } + + // Turns Acorn AST objects into POJOs, for use with Immer. + originalAst = JSON.parse(JSON.stringify(originalAst)); + + return function templatedAst(...replacementNodes: ArgTypes): ReturnType { + const result = produce(originalAst, (astDraft) => + traverse(astDraft, visitors, { + placeholderToValidator, + replacementNodes, + }) + ) as ReturnType; + return unwrap ? unwrap(result) : result; + }; +} + +export function esTemplate< + ReturnType = Node, + ArgTypes extends (Node | Node[] | null)[] = (Node | Node[] | null)[] +>( + javascriptSegments: TemplateStringsArray, + ...validatorFns: Validator[] +): (...replacementNodes: ArgTypes) => ReturnType { + return esTemplateImpl(javascriptSegments, validatorFns); +} + +export function esTemplateWithYield< + ReturnType = Node, + ArgTypes extends (Node | Node[] | null)[] = (Node | Node[] | null)[] +>( + javascriptSegments: TemplateStringsArray, + ...validatorFns: Validator[] +): (...replacementNodes: ArgTypes) => ReturnType { + const wrap = (code: string) => `function* placeholder() {${code}}`; + const unwrap = (node: FunctionDeclaration) => + node.body.body.length === 1 + ? (node.body.body[0] as ReturnType) + : (node.body.body as ReturnType); + + return esTemplateImpl( + javascriptSegments, + validatorFns, + wrap, + unwrap + ) as unknown as (...replacementNodes: ArgTypes) => ReturnType; +} diff --git a/packages/@lwc/ssr-compiler/src/estree/builders.ts b/packages/@lwc/ssr-compiler/src/estree/builders.ts new file mode 100644 index 0000000000..f91e174437 --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/estree/builders.ts @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import { is } from 'estree-toolkit'; +import { esTemplate } from '../estemplate'; +import { isStringLiteral } from './validators'; + +import type { ImportDeclaration, NewExpression } from 'estree'; + +export const bImportDeclaration = esTemplate` + import ${is.identifier} from "${isStringLiteral}"; +`; + +export const bInstantiate = esTemplate`new ${is.identifier}()`; diff --git a/packages/@lwc/ssr-compiler/src/estree/validators.ts b/packages/@lwc/ssr-compiler/src/estree/validators.ts new file mode 100644 index 0000000000..d9fde40769 --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/estree/validators.ts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import { is } from 'estree-toolkit'; + +import type { Node } from 'estree'; +import type { Validator } from '../estemplate'; + +export const isNullOrInstantiation = (node: Node | null) => + (is.literal(node) && node.value === null) || is.newExpression(node); + +export const isStringLiteral = (node: Node | null) => + is.literal(node) && typeof node.value === 'string'; + +export const isIdentOrRenderCall = (node: Node | null) => + is.identifier(node) || + (is.callExpression(node) && + is.memberExpression(node.callee) && + is.identifier(node.callee.property) && + node.callee.property.name === 'render'); + +export function isNullableOf(validator: Validator) { + return (node: Node | null) => node === null || (validator && validator(node)); +} diff --git a/packages/@lwc/ssr-compiler/src/generator.ts b/packages/@lwc/ssr-compiler/src/generator.ts new file mode 100644 index 0000000000..1ca951d633 --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/generator.ts @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import type { Instruction } from './shared'; +import type { Program } from 'estree'; + +export function bytecodeToEsTree(_instructions: Generator): Program { + return { + type: 'Program', + body: [], + sourceType: 'module', + }; +} diff --git a/packages/@lwc/ssr-compiler/src/index.ts b/packages/@lwc/ssr-compiler/src/index.ts new file mode 100644 index 0000000000..cf2e153286 --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/index.ts @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import compileJS from './compile-js'; +import compileTemplate from './compile-template'; + +import type { GenerateMarkupFn } from './shared'; + +interface TransformOptions {} + +export interface CompilationResult { + code: string; + map: unknown; +} + +export function compileComponentForSSR( + src: string, + filename: string, + _options: TransformOptions +): CompilationResult { + const { code } = compileJS(src, filename); + return { code, map: undefined }; +} + +export function compileTemplateForSSR( + src: string, + filename: string, + _options: TransformOptions +): CompilationResult { + const { code } = compileTemplate(src, filename); + return { + code, + map: undefined, + }; +} + +export async function serverSideRenderComponent( + tagName: string, + compiledGenerateMarkup: GenerateMarkupFn, + props: Record +): Promise { + let markup = ''; + + for await (const segment of compiledGenerateMarkup(tagName, props, null, null)) { + markup += segment; + } + + return markup; +} diff --git a/packages/@lwc/ssr-compiler/src/optimizer.ts b/packages/@lwc/ssr-compiler/src/optimizer.ts new file mode 100644 index 0000000000..975375a4fa --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/optimizer.ts @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +import type { Instruction } from './shared'; + +export function* optimize(instructions: Generator): Generator { + yield* instructions; +} diff --git a/packages/@lwc/ssr-compiler/src/shared.ts b/packages/@lwc/ssr-compiler/src/shared.ts new file mode 100644 index 0000000000..008f64d5d9 --- /dev/null +++ b/packages/@lwc/ssr-compiler/src/shared.ts @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +export type Expression = string; + +export type Instruction = + | IEmitTagName + | IEmitStaticString + | IEmitExpression + | IStartConditional + | IEndConditional + | IInvokeConnectedCallback + | IRenderChild + | IHoistImport + | IHoistInstantiation; + +export interface IEmitTagName { + kind: 'emitTagName'; +} + +export interface IEmitStaticString { + kind: 'emitStaticString'; +} + +export interface IEmitExpression { + kind: 'emitExpression'; + expression: Expression; +} + +export interface IStartConditional { + kind: 'startConditional'; +} + +export interface IEndConditional { + kind: 'endConditional'; +} + +export interface IInvokeConnectedCallback { + kind: 'invokeConnectedCallback'; +} + +export interface IRenderChild { + kind: 'renderChild'; + dynamic: Expression | null; +} + +export interface IHoistImport { + kind: 'hoistImport'; +} + +export interface IHoistInstantiation { + kind: 'hoistInstantiation'; +} + +export type GenerateMarkupFn = ( + tagName: string, + props: Record | null, + attrs: Record | null, + slotted: Record> | null +) => AsyncGenerator; + +export type TemplateFn = (Ctor: any, instance: any | null) => AsyncGenerator; diff --git a/packages/@lwc/ssr-compiler/tsconfig.json b/packages/@lwc/ssr-compiler/tsconfig.json new file mode 100644 index 0000000000..af627f6afe --- /dev/null +++ b/packages/@lwc/ssr-compiler/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../../tsconfig.json", + + "include": ["src/"] +} diff --git a/packages/@lwc/ssr-runtime/jest.config.js b/packages/@lwc/ssr-runtime/jest.config.js new file mode 100644 index 0000000000..5a74ed8393 --- /dev/null +++ b/packages/@lwc/ssr-runtime/jest.config.js @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2023, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ +const BASE_CONFIG = require('../../../scripts/jest/base.config'); + +module.exports = { + ...BASE_CONFIG, + displayName: 'lwc-ssr-runtime', +}; diff --git a/packages/@lwc/ssr-runtime/package.json b/packages/@lwc/ssr-runtime/package.json new file mode 100644 index 0000000000..13f8565f47 --- /dev/null +++ b/packages/@lwc/ssr-runtime/package.json @@ -0,0 +1,46 @@ +{ + "//": [ + "THIS FILE IS AUTOGENERATED. If you modify it, it will be rewritten by check-and-rewrite-package-json.js", + "You can safely modify dependencies, devDependencies, keywords, etc., but other props will be overwritten." + ], + "name": "@lwc/ssr-runtime", + "version": "6.1.1", + "description": "Runtime complement to @lwc/ssr-compiler", + "keywords": [ + "lwc" + ], + "homepage": "https://lwc.dev", + "repository": { + "type": "git", + "url": "https://github.com/salesforce/lwc.git", + "directory": "packages/@lwc/ssr-runtime" + }, + "bugs": { + "url": "https://github.com/salesforce/lwc/issues" + }, + "license": "MIT", + "publishConfig": { + "access": "public" + }, + "main": "dist/index.cjs.js", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "rollup --config ../../../scripts/rollup/rollup.config.js", + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", + "test": "jest ./src/**/*.spec.ts" + }, + "nx": { + "targets": { + "build": { + "outputs": [ + "{projectRoot}/dist" + ] + } + } + }, + "dependencies": {} +} diff --git a/packages/@lwc/ssr-runtime/src/index.ts b/packages/@lwc/ssr-runtime/src/index.ts new file mode 100644 index 0000000000..ff3fd7fa0c --- /dev/null +++ b/packages/@lwc/ssr-runtime/src/index.ts @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ + +const MULTI_SPACE = /\s+/g; + +type Attributes = Record; + +type LightningElementConstructor = typeof LightningElement & { + renderMode?: 'light' | 'shadow'; +}; + +class ClassList { + el: LightningElement; + + constructor(el: LightningElement) { + this.el = el; + } + + add(...newClassNames: string[]) { + const className = this.el.className; + const set = new Set(className.split(MULTI_SPACE).filter(Boolean)); + for (const newClassName of newClassNames) { + set.add(newClassName); + } + this.el.className = Array.from(set).join(' '); + } + + contains(className: string) { + const currentClassNameStr = this.el.className; + return currentClassNameStr.split(MULTI_SPACE).includes(className); + } + + remove(...classNamesToRemove: string[]) { + const className = this.el.className; + const set = new Set(className.split(MULTI_SPACE).filter(Boolean)); + for (const newClassName of classNamesToRemove) { + set.delete(newClassName); + } + this.el.className = Array.from(set).join(' '); + } + + replace(oldClassName: string, newClassName: string) { + let classWasReplaced = false; + const className = this.el.className; + const listOfClasses = className.split(MULTI_SPACE).filter(Boolean); + listOfClasses.forEach((value, idx) => { + if (value === oldClassName) { + classWasReplaced = true; + listOfClasses[idx] = newClassName; + } + }); + this.el.className = listOfClasses.join(' '); + return classWasReplaced; + } + + toggle(classNameToToggle: string, force?: boolean) { + const classNameStr = this.el.className; + const set = new Set(classNameStr.split(MULTI_SPACE).filter(Boolean)); + if (!set.has(classNameToToggle) && force !== false) { + set.add(classNameToToggle); + } else if (set.has(classNameToToggle) && force !== true) { + set.delete(classNameToToggle); + } + this.el.className = Array.from(set).join(' '); + return set.has(classNameToToggle); + } +} + +export class LightningElement { + isConnected = false; + className = ''; + // TODO [W-14977927]: protect internals from userland + __attrs?: Record; + __classList: ClassList | null = null; + + constructor(propsAvailableAtConstruction: Record) { + Object.assign(this, propsAvailableAtConstruction); + } + + // TODO [W-14977927]: protect internals from userland + __internal__setState( + props: Record, + reflectedProps: string[], + attrs: Record + ) { + Object.assign(this, props); + this.__attrs = attrs; + + // Whenever a reflected prop changes, we'll update the original props object + // that was passed in. That'll be referenced when the attrs are rendered later. + for (const reflectedPropName of reflectedProps) { + Object.defineProperty(this, reflectedPropName, { + get() { + return props[reflectedPropName]; + }, + set(newValue) { + props[reflectedPropName] = newValue; + }, + enumerable: true, + }); + } + + Object.defineProperty(this, 'className', { + get() { + return props.class ?? ''; + }, + set(newVal) { + props.class = newVal; + attrs.class = newVal; + }, + }); + } + + get classList() { + if (this.__classList) { + return this.__classList; + } + return (this.__classList = new ClassList(this)); + } + + getAttribute(attrName: string) { + return this.__attrs?.[attrName] ?? null; + } +} + +const escapeAttrVal = (attrVal: string) => + attrVal.replaceAll('&', '&').replaceAll('"', '"'); + +export function* renderAttrs(attrs: Attributes) { + if (!attrs) { + return; + } + for (const [key, val] of Object.entries(attrs)) { + if (val) { + if (typeof val === 'string') { + yield ` ${key}="${escapeAttrVal(val)}"`; + } else { + yield ` ${key}`; + } + } + } +} + +export function* fallbackTmpl( + _props: unknown, + _attrs: unknown, + _slotted: unknown, + Cmp: LightningElementConstructor, + _instance: unknown +) { + if (Cmp.renderMode !== 'light') { + yield ''; + } +} diff --git a/packages/@lwc/ssr-runtime/tsconfig.json b/packages/@lwc/ssr-runtime/tsconfig.json new file mode 100644 index 0000000000..fd932d58c3 --- /dev/null +++ b/packages/@lwc/ssr-runtime/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../../tsconfig.json", + + "compilerOptions": { + "lib": ["es2021"] + }, + + "include": ["src/"] +} diff --git a/packages/@lwc/template-compiler/src/index.ts b/packages/@lwc/template-compiler/src/index.ts index f54f01b42d..ba4c1dc7fc 100644 --- a/packages/@lwc/template-compiler/src/index.ts +++ b/packages/@lwc/template-compiler/src/index.ts @@ -23,6 +23,9 @@ export * from './shared/types'; export { CustomRendererConfig, CustomRendererElementConfig } from './shared/renderer-hooks'; export { Config } from './config'; +export { toPropertyName } from './shared/utils'; +export { kebabcaseToCamelcase } from './shared/naming'; + export function parse(source: string, config: Config = {}): TemplateParseResult { const options = normalizeConfig(config); const state = new State(options); diff --git a/scripts/rollup/rollup.config.js b/scripts/rollup/rollup.config.js index 3bf0fcc03d..c8ac707470 100644 --- a/scripts/rollup/rollup.config.js +++ b/scripts/rollup/rollup.config.js @@ -43,6 +43,10 @@ if (packageName === '@lwc/synthetic-shadow') { } const onwarn = ({ code, message }) => { + if (code === 'INVALID_ANNOTATION') { + // Not sure why the PURE annotations are broken. + return; + } if (!process.env.ROLLUP_WATCH && code !== 'CIRCULAR_DEPENDENCY') { throw new Error(message); } diff --git a/yarn.lock b/yarn.lock index 0e969a6035..e841fb3967 100644 --- a/yarn.lock +++ b/yarn.lock @@ -360,7 +360,7 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== -"@babel/parser@^7.23.6", "@babel/parser@^7.23.9": +"@babel/parser@^7.23.6", "@babel/parser@^7.23.9", "@babel/parser@~7.23.6": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.9.tgz#7b903b6149b0f8fa7ad564af646c4c38a77fc44b" integrity sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA== @@ -551,7 +551,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.23.6", "@babel/traverse@^7.23.9": +"@babel/traverse@^7.23.6", "@babel/traverse@^7.23.9", "@babel/traverse@~7.23.6": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.9.tgz#2f9d6aead6b564669394c5ce0f9302bb65b9d950" integrity sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg== @@ -585,7 +585,7 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" -"@babel/types@^7.23.9": +"@babel/types@^7.23.9", "@babel/types@~7.23.6": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.9.tgz#1dd7b59a9a2b5c87f8b41e52770b5ecbf492e002" integrity sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q== @@ -1340,6 +1340,18 @@ version "0.0.0" uid "" +"@lwc/metadata@6.1.0-0": + version "6.1.0-0" + resolved "https://registry.yarnpkg.com/@lwc/metadata/-/metadata-6.1.0-0.tgz#0520afc921a773ff3d757006f651f14f4e8877b1" + integrity sha512-MPjv4fsXZ22FbDV4W9Ry2UAByXAHVTKV3pxFQ0nRAiyBlt6l2Q5/fIKS9ehhrtsGMETNol63FmcLh+JEm/JrLg== + dependencies: + "@babel/parser" "~7.23.6" + "@babel/traverse" "~7.23.6" + "@babel/types" "~7.23.6" + postcss "~8.4.32" + postcss-selector-parser "~6.0.13" + postcss-value-parser "~4.2.0" + "@lwc/module-resolver@5.3.0": version "5.3.0" resolved "https://registry.yarnpkg.com/@lwc/module-resolver/-/module-resolver-5.3.0.tgz#3e8ef877d4c5a7ff08db7f75332335f17cdf2967" @@ -1356,6 +1368,11 @@ "@lwc/module-resolver" "5.3.0" "@rollup/pluginutils" "~5.1.0" +"@lwc/sfdc-compiler-utils@6.1.0-0": + version "6.1.0-0" + resolved "https://registry.yarnpkg.com/@lwc/sfdc-compiler-utils/-/sfdc-compiler-utils-6.1.0-0.tgz#dc712225a9630602a101027e202cf69d79fcdae7" + integrity sha512-iC28GJ0Zd5GIPVnRO9Aof5r4Um6hpxfVIZgUXYkmgHFwo/uT+eJ5pqbEg/MB3MM6Vo1J2NxpO3uqZzlYVUabmA== + "@lwc/shared@5.3.0": version "5.3.0" resolved "https://registry.yarnpkg.com/@lwc/shared/-/shared-5.3.0.tgz#994662e803da7a2afd7dc1ae1ded6bbe79f85d75" @@ -2000,12 +2017,14 @@ dependencies: "@types/node" "*" -"@types/estree@*", "@types/estree@^1.0.0": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.2.tgz#ff02bc3dc8317cd668dfec247b750ba1f1d62453" - integrity sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA== +"@types/estree-jsx@^1.0.0": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@types/estree-jsx/-/estree-jsx-1.0.4.tgz#8d34b43444887dde8a73af530f772f23e1d3287c" + integrity sha512-5idy3hvI9lAMqsyilBM+N+boaCf1MgoefbDxN6KEO5aK17TOHwFAYT9sjxzeKAiIWRUBgLxmZ9mPcnzZXtTcRQ== + dependencies: + "@types/estree" "*" -"@types/estree@1.0.5": +"@types/estree@*", "@types/estree@1.0.5", "@types/estree@^1.0.0", "@types/estree@^1.0.1", "@types/estree@^1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== @@ -3068,7 +3087,7 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -astring@~1.8.6: +astring@^1.8.6, astring@~1.8.6: version "1.8.6" resolved "https://registry.yarnpkg.com/astring/-/astring-1.8.6.tgz#2c9c157cf1739d67561c56ba896e6948f6b93731" integrity sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg== @@ -5144,6 +5163,14 @@ estraverse@^5.1.0, estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== +estree-toolkit@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/estree-toolkit/-/estree-toolkit-1.7.3.tgz#3cf0213779349b020b49884fd9e4c32ee80cbec3" + integrity sha512-hMPO2kBkGtZK7GDUeouqm01kCzIWyk0b5gJFunCpbafHSQyB45u2/cYgxt5U5w+0Qt/AsqdlWhoDNdyWlmeWjw== + dependencies: + "@types/estree" "^1.0.1" + "@types/estree-jsx" "^1.0.0" + estree-walker@^2.0.2, estree-walker@~2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" @@ -6459,6 +6486,11 @@ immediate@~3.0.5: resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== +immer@^10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/immer/-/immer-10.0.3.tgz#a8de42065e964aa3edf6afc282dfc7f7f34ae3c9" + integrity sha512-pwupu3eWfouuaowscykeckFmVTpqbzW+rXFCX8rQLkZzM9ftBmU/++Ra+o+L27mz03zJTlyV4UUr+fdKNffo4A== + import-fresh@^3.0.0, import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -8342,6 +8374,11 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +meriyah@^4.3.8: + version "4.3.9" + resolved "https://registry.yarnpkg.com/meriyah/-/meriyah-4.3.9.tgz#2f5a3ec266cccbbe04e7bdc08d1ba7ee3bc38475" + integrity sha512-IepKx25Ph/5T67o+Q6Ezrt+repNAcJsydfsNWgelrdOhdcQcs4NI9qPHxB9Qz3WSYpj77hScNJrq5IjdwE1woQ== + methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" From 3f1677a9473d76dbd06b36b7b868cd266ecfa718 Mon Sep 17 00:00:00 2001 From: Dale Bustad Date: Thu, 8 Feb 2024 11:37:16 -0800 Subject: [PATCH 04/13] chore: address PR feedback chore: add new keywords to package.json Co-authored-by: Will Harney <62956339+wjhsf@users.noreply.github.com> chore: remove unnecessary non-null assertion Co-authored-by: Will Harney <62956339+wjhsf@users.noreply.github.com> chore: improve internal type signature chore: address minor PR feedback chore: address more PR feedback chore: move serverSideRenderComponent to @lwc/ssr-runtime chore: pretty chore: remove acorn from @lwc/ssr-compiler chore: remove the test command from subpackages chore: move reserved keywords over to @lwc/shared chore: revert unnecessary rollup change chore: remove non-null assertions chore: address PR feedback chore: address PR feedback chore: address PR feedback chore: address PR feedback chore: clean up types in estemplate chore: minor cleanup chore: support backslash path separators chore: remove redundant NonNullable chore: minor cleanup fix: prop values of null or undefined should be rendered as empty text node chore: add a bunch of comments --- packages/@lwc/aria-reflection/package.json | 3 +- .../@lwc/babel-plugin-component/package.json | 3 +- packages/@lwc/compiler/package.json | 3 +- packages/@lwc/engine-core/package.json | 3 +- packages/@lwc/engine-dom/package.json | 3 +- packages/@lwc/engine-server/package.json | 3 +- packages/@lwc/errors/package.json | 3 +- packages/@lwc/features/package.json | 3 +- packages/@lwc/module-resolver/package.json | 3 +- packages/@lwc/rollup-plugin/package.json | 3 +- packages/@lwc/shared/package.json | 3 +- packages/@lwc/shared/src/ecmascript.ts | 57 ++++++++++++++++++ packages/@lwc/shared/src/index.ts | 1 + packages/@lwc/ssr-compiler/package.json | 8 +-- .../src/__tests__/fixtures.spec.ts | 23 ++++---- .../src/compile-js/catalog-tmpls.ts | 19 ++++-- .../src/compile-js/generate-markup.ts | 29 +++++++--- .../@lwc/ssr-compiler/src/compile-js/index.ts | 15 +++-- .../ssr-compiler/src/compile-js/lwc-import.ts | 13 +++-- .../src/compile-js/stylesheets.ts | 5 +- .../@lwc/ssr-compiler/src/compile-js/types.ts | 14 ++++- .../src/compile-template/context.ts | 8 +-- .../src/compile-template/element.ts | 22 +++---- .../src/compile-template/index.ts | 2 +- .../src/compile-template/shared.ts | 58 ++----------------- .../ssr-compiler/src/compile-template/text.ts | 2 +- packages/@lwc/ssr-compiler/src/estemplate.ts | 55 +++++++++--------- packages/@lwc/ssr-compiler/src/generator.ts | 17 ------ packages/@lwc/ssr-compiler/src/index.ts | 21 +------ packages/@lwc/ssr-compiler/src/shared.ts | 9 --- packages/@lwc/ssr-runtime/package.json | 7 ++- packages/@lwc/ssr-runtime/src/index.ts | 34 ++++++++--- packages/@lwc/style-compiler/package.json | 3 +- packages/@lwc/synthetic-shadow/package.json | 3 +- packages/@lwc/template-compiler/package.json | 3 +- packages/@lwc/wire-service/package.json | 3 +- scripts/rollup/rollup.config.js | 4 -- .../tasks/check-and-rewrite-package-json.js | 2 - yarn.lock | 2 +- 39 files changed, 239 insertions(+), 233 deletions(-) create mode 100644 packages/@lwc/shared/src/ecmascript.ts delete mode 100644 packages/@lwc/ssr-compiler/src/generator.ts diff --git a/packages/@lwc/aria-reflection/package.json b/packages/@lwc/aria-reflection/package.json index 9163727a34..6e20db25fb 100644 --- a/packages/@lwc/aria-reflection/package.json +++ b/packages/@lwc/aria-reflection/package.json @@ -34,8 +34,7 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", - "test": "jest ./src/**/*.spec.ts" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" }, "nx": { "targets": { diff --git a/packages/@lwc/babel-plugin-component/package.json b/packages/@lwc/babel-plugin-component/package.json index 56a45d9026..9298cac332 100644 --- a/packages/@lwc/babel-plugin-component/package.json +++ b/packages/@lwc/babel-plugin-component/package.json @@ -30,8 +30,7 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", - "test": "jest ./src/**/*.spec.ts" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" }, "nx": { "targets": { diff --git a/packages/@lwc/compiler/package.json b/packages/@lwc/compiler/package.json index 7ac6d0d271..e4d8aef063 100755 --- a/packages/@lwc/compiler/package.json +++ b/packages/@lwc/compiler/package.json @@ -30,8 +30,7 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", - "test": "jest ./src/**/*.spec.ts" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" }, "nx": { "targets": { diff --git a/packages/@lwc/engine-core/package.json b/packages/@lwc/engine-core/package.json index 9022104238..72b12ca945 100644 --- a/packages/@lwc/engine-core/package.json +++ b/packages/@lwc/engine-core/package.json @@ -30,8 +30,7 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", - "test": "jest ./src/**/*.spec.ts" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" }, "nx": { "targets": { diff --git a/packages/@lwc/engine-dom/package.json b/packages/@lwc/engine-dom/package.json index 6410389777..a334db7c7d 100644 --- a/packages/@lwc/engine-dom/package.json +++ b/packages/@lwc/engine-dom/package.json @@ -30,8 +30,7 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", - "test": "jest ./src/**/*.spec.ts" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" }, "nx": { "targets": { diff --git a/packages/@lwc/engine-server/package.json b/packages/@lwc/engine-server/package.json index dac568a845..a92ca7b780 100644 --- a/packages/@lwc/engine-server/package.json +++ b/packages/@lwc/engine-server/package.json @@ -30,8 +30,7 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", - "test": "jest ./src/**/*.spec.ts" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" }, "nx": { "targets": { diff --git a/packages/@lwc/errors/package.json b/packages/@lwc/errors/package.json index a5505532cb..41f03de9e4 100644 --- a/packages/@lwc/errors/package.json +++ b/packages/@lwc/errors/package.json @@ -30,8 +30,7 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", - "test": "jest ./src/**/*.spec.ts" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" }, "nx": { "targets": { diff --git a/packages/@lwc/features/package.json b/packages/@lwc/features/package.json index a21ec501d5..05996b0aa5 100644 --- a/packages/@lwc/features/package.json +++ b/packages/@lwc/features/package.json @@ -30,8 +30,7 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", - "test": "jest ./src/**/*.spec.ts" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" }, "nx": { "targets": { diff --git a/packages/@lwc/module-resolver/package.json b/packages/@lwc/module-resolver/package.json index bee3982ac3..f7ac4c57de 100644 --- a/packages/@lwc/module-resolver/package.json +++ b/packages/@lwc/module-resolver/package.json @@ -30,8 +30,7 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", - "test": "jest ./src/**/*.spec.ts" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" }, "nx": { "targets": { diff --git a/packages/@lwc/rollup-plugin/package.json b/packages/@lwc/rollup-plugin/package.json index 3f9d0c2e9a..4f1da7a7db 100644 --- a/packages/@lwc/rollup-plugin/package.json +++ b/packages/@lwc/rollup-plugin/package.json @@ -30,8 +30,7 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", - "test": "jest ./src/**/*.spec.ts" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" }, "nx": { "targets": { diff --git a/packages/@lwc/shared/package.json b/packages/@lwc/shared/package.json index 350923974c..039ed6a818 100644 --- a/packages/@lwc/shared/package.json +++ b/packages/@lwc/shared/package.json @@ -30,8 +30,7 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", - "test": "jest ./src/**/*.spec.ts" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" }, "nx": { "targets": { diff --git a/packages/@lwc/shared/src/ecmascript.ts b/packages/@lwc/shared/src/ecmascript.ts new file mode 100644 index 0000000000..1735e79767 --- /dev/null +++ b/packages/@lwc/shared/src/ecmascript.ts @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: MIT + * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT + */ +export const reservedKeywords = new Set([ + 'NaN', + 'arguments', + 'break', + 'case', + 'catch', + 'class', + 'const', + 'continue', + 'debugger', + 'default', + 'delete', + 'do', + 'else', + 'enum', + 'eval', + 'export', + 'extends', + 'false', + 'finally', + 'for', + 'function', + 'if', + 'implements', + 'import', + 'in', + 'instanceof', + 'interface', + 'let', + 'new', + 'null', + 'package', + 'private', + 'protected', + 'public', + 'return', + 'static', + 'super', + 'switch', + 'this', + 'throw', + 'true', + 'try', + 'typeof', + 'undefined', + 'var', + 'void', + 'while', + 'with', + 'yield', +]); diff --git a/packages/@lwc/shared/src/index.ts b/packages/@lwc/shared/src/index.ts index b5e7e918fd..49d2ea83c6 100644 --- a/packages/@lwc/shared/src/index.ts +++ b/packages/@lwc/shared/src/index.ts @@ -8,6 +8,7 @@ import * as assert from './assert'; export * from './api-version'; export * from './aria'; +export * from './ecmascript'; export * from './language'; export * from './keys'; export * from './void-elements'; diff --git a/packages/@lwc/ssr-compiler/package.json b/packages/@lwc/ssr-compiler/package.json index da15c66a64..c68998aaf7 100644 --- a/packages/@lwc/ssr-compiler/package.json +++ b/packages/@lwc/ssr-compiler/package.json @@ -7,7 +7,9 @@ "version": "6.1.1", "description": "Compile component for use during server-side rendering", "keywords": [ - "lwc" + "compiler", + "lwc", + "ssr" ], "homepage": "https://lwc.dev", "repository": { @@ -30,8 +32,7 @@ ], "scripts": { "build": "rollup --config ../../../scripts/rollup/rollup.config.js", - "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen", - "test-manual": "jest ./src/**/*.spec.ts" + "dev": "rollup --config ../../../scripts/rollup/rollup.config.js --watch --no-watch.clearScreen" }, "nx": { "targets": { @@ -47,7 +48,6 @@ "@lwc/sfdc-compiler-utils": "6.1.0-0", "@lwc/style-compiler": "6.1.1", "@lwc/template-compiler": "6.1.1", - "acorn": "~8.10.0", "astring": "^1.8.6", "estree-toolkit": "^1.7.3", "immer": "^10.0.3", diff --git a/packages/@lwc/ssr-compiler/src/__tests__/fixtures.spec.ts b/packages/@lwc/ssr-compiler/src/__tests__/fixtures.spec.ts index 432e716b43..4e18d3e6fa 100644 --- a/packages/@lwc/ssr-compiler/src/__tests__/fixtures.spec.ts +++ b/packages/@lwc/ssr-compiler/src/__tests__/fixtures.spec.ts @@ -9,18 +9,18 @@ import fs from 'fs'; import path from 'path'; import { rollup, RollupLog } from 'rollup'; -// @ts-ignore import lwcRollupPlugin from '@lwc/rollup-plugin'; import { isVoidElement, HTML_NAMESPACE } from '@lwc/shared'; +import { FeatureFlagName } from '@lwc/features/dist/types'; import { testFixtureDir } from '@lwc/jest-utils-lwc-internals'; -import { serverSideRenderComponent } from '../index'; +import { serverSideRenderComponent } from '@lwc/ssr-runtime'; interface FixtureModule { tagName: string; default: any; generateMarkup: any; props?: { [key: string]: any }; - features?: any[]; + features?: FeatureFlagName[]; } jest.setTimeout(10_000 /* 10 seconds */); @@ -82,13 +82,19 @@ function formatHTML(src: string): string { if (src.charAt(pos) === '<') { const tagNameMatch = src.slice(pos).match(/(\w+)/); + if (!tagNameMatch) { + throw new Error( + `Expected to find tagname at pos ${pos} but found "${src.slice(pos, 20)}"` + ); + } + // Special handling for `