Skip to content

Commit

Permalink
chore: resolve minor feedback from #4685 (#4700)
Browse files Browse the repository at this point in the history
* chore: add ssrMode to rollup plugin README

* chore: use `asyncYield` as default ssrMode

* chore: define `emit` function once

* chore: use asyncYield mode in SSR compiler tests for now

* test(ssr-compiler): correct transmogrify behavior

* chore: add some whitespace for readability
  • Loading branch information
divmain authored Oct 25, 2024
1 parent fa6ef21 commit 75ff766
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 8 deletions.
2 changes: 1 addition & 1 deletion packages/@lwc/compiler/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const DEFAULT_OPTIONS = {
disableSyntheticShadowSupport: false,
enableLightningWebSecurityTransforms: false,
targetSSR: false,
ssrMode: 'sync',
ssrMode: 'asyncYield',
} as const;

const DEFAULT_DYNAMIC_IMPORT_CONFIG: Required<DynamicImportConfig> = {
Expand Down
1 change: 1 addition & 0 deletions packages/@lwc/rollup-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ export default {
- `apiVersion` (type: `number`, default: `undefined`) - Set to a valid API version such as 58, 59, etc. This will be overriden if the component itself overrides the version with a `*.js-meta.xml` file.
- `enableStaticContentOptimization` (type: `boolean`, optional) - True if the static content optimization should be enabled. Passed to `@lwc/template-compiler`. True by default.
- `targetSSR` (type: `boolean`) - Utilize the experimental SSR compilation mode. False by default. Do not use unless you know what you're doing.
- `ssrMode` (type: `string`): The variety of SSR code that should be generated when using experimental SSR compilation mode. Should be one of `sync`, `async` or `asyncYield`.
2 changes: 1 addition & 1 deletion packages/@lwc/ssr-compiler/src/__tests__/fixtures.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ interface FixtureModule {

vi.setConfig({ testTimeout: 10_000 /* 10 seconds */ });

const SSR_MODE: CompilationMode = 'sync';
const SSR_MODE: CompilationMode = 'asyncYield';

async function compileFixture({ input, dirname }: { input: string; dirname: string }) {
const modulesDir = path.resolve(dirname, './modules');
Expand Down
138 changes: 138 additions & 0 deletions packages/@lwc/ssr-compiler/src/__tests__/transmogrify.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { parseModule } from 'meriyah';
import { generate } from 'astring';
import { beforeAll } from 'vitest';
import { transmogrify } from '../transmogrify';
import type { Program as EsProgram } from 'estree';

const COMPILED_CMP = `
async function* tmpl(props, attrs, slottedContent, Cmp, instance) {
yield "<p";
yield stylesheetScopeTokenClass;
yield ">Hello</p>";
}
function* somethingElse() {
yield "doit";
}
async function* something() {
yield "foobar";
yield* somethingElse();
}
class Basic extends LightningElement {
static renderMode = "light";
async connectedCallback() {
for await (const thing of something()) {
console.log(thing);
}
}
}
const __REFLECTED_PROPS__ = [];
async function* generateMarkup(tagName, props, attrs, slotted) {
attrs = attrs ?? ({});
const instance = new Basic({
tagName: tagName.toUpperCase()
});
instance[SYMBOL__SET_INTERNALS](props, __REFLECTED_PROPS__, attrs);
instance.isConnected = true;
if (instance.connectedCallback) {
mutationTracker.enable(instance);
instance.connectedCallback();
mutationTracker.disable(instance);
}
const tmplFn = tmpl ?? fallbackTmpl;
yield \`<\${tagName}\`;
yield tmplFn.stylesheetScopeTokenHostClass ?? '';
yield* renderAttrs(instance, attrs);
yield '>';
yield* tmplFn(props, attrs, slotted, Basic, instance);
yield \`</\${tagName}>\`;
}
`;

describe('transmogrify', () => {
let COMPILED_CMP_SYNC: string;
let COMPILED_CMP_ASYNC: string;

beforeAll(() => {
const COMPILED_CMP_AST = parseModule(COMPILED_CMP, {
module: true,
next: true,
}) as EsProgram;
const COMPILED_CMP_AST_SYNC = transmogrify(COMPILED_CMP_AST, 'sync');
const COMPILED_CMP_AST_ASYNC = transmogrify(COMPILED_CMP_AST, 'async');
COMPILED_CMP_SYNC = generate(COMPILED_CMP_AST_SYNC);
COMPILED_CMP_ASYNC = generate(COMPILED_CMP_AST_ASYNC);
});

describe('in sync mode', () => {
test('generateMarkup is transformed into sync mode', () => {
expect(COMPILED_CMP_SYNC).not.toContain('async function* generateMarkup');
expect(COMPILED_CMP_SYNC).not.toContain('async function generateMarkup');
expect(COMPILED_CMP_SYNC).toContain('function generateMarkup($$emit');

expect(COMPILED_CMP_SYNC).not.toContain('yield* renderAttrs');
expect(COMPILED_CMP_SYNC).toContain('renderAttrs($$emit');

expect(COMPILED_CMP_SYNC).not.toContain('yield ">"');
expect(COMPILED_CMP_SYNC).not.toContain("yield '>'");
expect(COMPILED_CMP_SYNC).toContain('$$emit(">")');
});

test('tmpl is transformed into sync mode', () => {
expect(COMPILED_CMP_SYNC).not.toContain('async function* tmpl');
expect(COMPILED_CMP_SYNC).not.toContain('async function tmpl');
expect(COMPILED_CMP_SYNC).toContain('function tmpl($$emit');

expect(COMPILED_CMP_SYNC).not.toContain('yield "<p"');
expect(COMPILED_CMP_SYNC).toContain('$$emit("<p")');

expect(COMPILED_CMP_SYNC).not.toContain('yield stylesheetScopeTokenClass');
expect(COMPILED_CMP_SYNC).toContain('$$emit(stylesheetScopeTokenClass)');
});

test('component code is not transformed into sync mode', () => {
expect(COMPILED_CMP_SYNC).toContain('async connectedCallback() {');
expect(COMPILED_CMP_SYNC).toContain('for await (const thing of something())');
expect(COMPILED_CMP_SYNC).toContain('function* somethingElse()');
expect(COMPILED_CMP_SYNC).toContain('async function* something()');
expect(COMPILED_CMP_SYNC).toContain('yield "doit"');
expect(COMPILED_CMP_SYNC).toContain('yield "foobar"');
expect(COMPILED_CMP_SYNC).toContain('yield* somethingElse()');
});
});

describe('in async mode', () => {
test('generateMarkup is transformed into async mode', () => {
expect(COMPILED_CMP_ASYNC).not.toContain('async function* generateMarkup');
expect(COMPILED_CMP_ASYNC).toContain('async function generateMarkup($$emit');

expect(COMPILED_CMP_ASYNC).not.toContain('yield* renderAttrs');
expect(COMPILED_CMP_ASYNC).toContain('renderAttrs($$emit');

expect(COMPILED_CMP_ASYNC).not.toContain('yield ">"');
expect(COMPILED_CMP_ASYNC).not.toContain("yield '>'");
expect(COMPILED_CMP_ASYNC).toContain('$$emit(">")');
});

test('tmpl is transformed into async mode', () => {
expect(COMPILED_CMP_ASYNC).not.toContain('async function* tmpl');
expect(COMPILED_CMP_ASYNC).toContain('async function tmpl($$emit');

expect(COMPILED_CMP_ASYNC).not.toContain('yield "<p"');
expect(COMPILED_CMP_ASYNC).toContain('$$emit("<p")');

expect(COMPILED_CMP_ASYNC).not.toContain('yield stylesheetScopeTokenClass');
expect(COMPILED_CMP_ASYNC).toContain('$$emit(stylesheetScopeTokenClass)');
});

test('component code is not transformed into async mode', () => {
expect(COMPILED_CMP_ASYNC).toContain('async connectedCallback() {');
expect(COMPILED_CMP_ASYNC).toContain('for await (const thing of something())');
expect(COMPILED_CMP_ASYNC).toContain('function* somethingElse()');
expect(COMPILED_CMP_ASYNC).toContain('async function* something()');
expect(COMPILED_CMP_ASYNC).toContain('yield "doit"');
expect(COMPILED_CMP_ASYNC).toContain('yield "foobar"');
expect(COMPILED_CMP_ASYNC).toContain('yield* somethingElse()');
});
});
});
9 changes: 3 additions & 6 deletions packages/@lwc/ssr-runtime/src/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ export async function serverSideRenderComponent(
mode: 'asyncYield' | 'async' | 'sync' = 'asyncYield'
): Promise<string> {
let markup = '';
const emit = (segment: string) => {
markup += segment;
};

if (mode === 'asyncYield') {
for await (const segment of (compiledGenerateMarkup as GenerateMarkupFn)(
Expand All @@ -117,9 +120,6 @@ export async function serverSideRenderComponent(
markup += segment;
}
} else if (mode === 'async') {
const emit = (segment: string) => {
markup += segment;
};
await (compiledGenerateMarkup as GenerateMarkupFnAsyncNoGen)(
emit,
tagName,
Expand All @@ -128,9 +128,6 @@ export async function serverSideRenderComponent(
null
);
} else if (mode === 'sync') {
const emit = (segment: string) => {
markup += segment;
};
(compiledGenerateMarkup as GenerateMarkupFnSyncNoGen)(emit, tagName, props, null, null);
} else {
throw new Error(`Invalid mode: ${mode}`);
Expand Down

0 comments on commit 75ff766

Please sign in to comment.