-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enhance: use html parser in preprocessor #240
Conversation
@@ -1,11 +1,4 @@ | |||
import { inlinePreprocessedSvelteComponent, escapeHtml, inlineSvelteComponent } from '../inlineSvelteComponent'; | |||
|
|||
test('#escapeHtml', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
escapeHtml
and replaceSpecialCharacters
are moved to htmlParser.ts
.
@@ -8,8 +8,8 @@ describe('#partialHydration', () => { | |||
content: '<DatePicker hydrate-client={{ a: "b" }} />', | |||
}) | |||
).code, | |||
).toEqual( | |||
`<div class="ejs-component" data-ejs-component="DatePicker" data-ejs-props={JSON.stringify({ a: "b" })} data-ejs-options={JSON.stringify({"loading":"lazy","element":"div"})} />`, | |||
).toMatchInlineSnapshot( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switch to inline snapshot so it is easier to update.
await expect(async () => { | ||
await partialHydration.markup({ | ||
content: '<DatePicker hydrate-client="string />', | ||
}); | ||
}).rejects.toThrow(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not valid svelte syntax so I just let preprocessor throw.
@@ -50,6 +41,8 @@ export default function mountComponentsInHtml({ page, html, hydrateOptions }): s | |||
page, | |||
props: hydrateComponentProps, | |||
hydrateOptions: hydrateComponentOptions, | |||
otherAttributes: match[5], | |||
openTagOnly: !match[6] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, we check whether the wrapper closes immediately to decide if the component has to be rendered again (i.e. shortcode component).
This doesn't work if the component renders into an empty string. Though it shouldn't harm either.
} | ||
} | ||
const spreadClientProps = clientProps ? ` {...(${clientProps})}` : ''; | ||
// FIXME: it should be possible to merge three attributes into one |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally, we should be able to generate simpler code in the preprocessor e.g.
`<div ejs-component=${stringifyExpression(`["${tag.name}",${clientProps},${options}]`)}>`
Then we just have to search for ejs-component
to extract all information in mountComponentsInHtml.ts
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely. This would be a huge simplification.
} | ||
const spreadClientProps = clientProps ? ` {...(${clientProps})}` : ''; | ||
// FIXME: it should be possible to merge three attributes into one | ||
// FIXME: use hydrateOptions.element instead of 'div' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hydrateOptions.element
no longer works in this PR. I didn't find the document for it so I'm not sure how to implement it correctly.
If this is needed, we can try to find the element via regex:
const rx = /\belement['"]?\s*:\s*['"]([^'"]+)/i;
const element = options.match(rx)?.[1];
This will work if the options is written in the source:
hydrate-options={{element: "span"}}
hydrate-options={{"element": "span"}}
But won't work in other cases:
<script>
const o = {element: 'span'};
</script>
<Component hydrate-client hydrate-options={o} />
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hydrate-options={{element: "span"}}
hydrate-options={{"element": "span"}}
This was the implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another solution is to use a special tag e.g.
<ejswrapper ... ></ejswrapper>
And replace it with the real tag name in mountComponentsInHtml
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eight04 Not strongly opinionated either way. Whatever you think is best.
const repl = createReplacementString(content, tag); | ||
s.overwrite(tag.start, tag.end, repl); | ||
dirty = true; | ||
} | ||
|
||
const wrappingComponentPattern = /<([a-zA-Z]+)[^>]+hydrate-client={([^]*?})}[^/>]*>[^>]*<\/([a-zA-Z]+)>/gim; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may not need this anymore?
It seems that there is a linting error that requires changing dependencies:
|
@eight04 I am a bit unclear on how "server props" are getting passed into the SSR call on |
); | ||
let dirty = false; | ||
const hydrateableComponentPattern = /<([a-zA-Z]+)[^>]+hydrate-client/gim; | ||
const s = new MagicString(content); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work here. Huge simplification.
@eight04 looks good to me once we sort out how to get the |
Yeah I think the current state of SSR props is not ideal. I will revert those changes. Currently it is built on svelte syntax, hence it will only work with svelte component, which against #237 (comment). We should probably introduce a new attribute like <Foo hydrate-client={{a: 1, b: 2}} hydrate-server={{a: 2}} />
<!--
ssr will get {a: 2, b: 2}
client will get {a: 1, b: 2}
--> Maybe to discuss ssr props further in #234. Still WIP. Things need to be done in this PR:
|
src/Elder.ts
Outdated
@@ -33,7 +33,7 @@ import { | |||
} from './utils/types'; | |||
import createReadOnlyProxy from './utils/createReadOnlyProxy'; | |||
import workerBuild from './workerBuild'; | |||
import { inlineSvelteComponent } from './partialHydration/inlineSvelteComponent'; | |||
import { inlineComponent } from './partialHydration/inlineComponent'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename inlineSvelteComponent
to inlineComponent
.
"id": "0", | ||
"name": "0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that the target ID is duplicated. Should we remove one of them?
initComponent(entry.target,selected) | ||
} | ||
}); | ||
}, { rootMargin: "200px",threshold: 0}) : undefined;` | ||
: '' | ||
} | ||
Object.keys(par).forEach(k => { | ||
const el = document.getElementById(k); | ||
const el = document.querySelector(\`[ejs-id="\${k}"]\`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use ejs-id
to target the mount target.
@@ -220,6 +220,8 @@ class Page { | |||
|
|||
componentsToHydrate: IComponentToHydrate[]; | |||
|
|||
getUniqueId = prepareGetUniqueId(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A new method is added. It generates ID incrementally so we don't have to deal with random ID in the test and generate reproducible build.
@@ -76,7 +76,7 @@ describe('#svelteComponent', () => { | |||
expect(home(componentProps)).toEqual(`<div class="svelte-home">mock html output</div>`); | |||
}); | |||
|
|||
it('svelteComponent works with partial hydration of subcomponent', () => { | |||
it.skip('svelteComponent works with partial hydration of subcomponent', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We will probably remove svelteComponent
in another PR so I didn't fix these tests.
Fixes #226
Fixes #227
Fixes #233
Noticeable changes:
<DatePicker hydrate-client />
.hydrate-options
is determined at render time hence 1.6.5 Unexpected token l in JSON at position 2 #227 is resolved.