Skip to content

Commit

Permalink
fix(hydrate): support style modes in hydrate modules (#5953)
Browse files Browse the repository at this point in the history
* fix(hydrate): support style modes in hydrate modules

* prettier

* properly resolve app-data module

* support setting multiple modes

* remove magic string

* fix linting
  • Loading branch information
christian-bromann committed Aug 30, 2024
1 parent 5cddfd9 commit 15f3b26
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ import { RollupOptions } from 'rollup';
import { rollup, type RollupBuild } from 'rollup';

import {
STENCIL_APP_DATA_ID,
STENCIL_HYDRATE_FACTORY_ID,
STENCIL_INTERNAL_HYDRATE_ID,
STENCIL_MOCK_DOC_ID,
} from '../../bundle/entry-alias-ids';
import { bundleHydrateFactory } from './bundle-hydrate-factory';
import { HYDRATE_FACTORY_INTRO, HYDRATE_FACTORY_OUTRO } from './hydrate-factory-closure';
import {
HYDRATE_FACTORY_INTRO,
HYDRATE_FACTORY_OUTRO,
MODE_RESOLUTION_CHAIN_DECLARATION,
} from './hydrate-factory-closure';
import { updateToHydrateComponents } from './update-to-hydrate-components';
import { writeHydrateOutputs } from './write-hydrate-outputs';

Expand Down Expand Up @@ -50,6 +55,7 @@ export const generateHydrateApp = async (
const packageDir = join(config.sys.getCompilerExecutingPath(), '..', '..');
const input = join(packageDir, 'internal', 'hydrate', 'runner.js');
const mockDoc = join(packageDir, 'mock-doc', 'index.js');
const appData = join(packageDir, 'internal', 'app-data', 'index.js');

const rollupOptions: RollupOptions = {
...config.rollupConfig.inputOptions,
Expand All @@ -67,6 +73,9 @@ export const generateHydrateApp = async (
if (id === STENCIL_MOCK_DOC_ID) {
return mockDoc;
}
if (id === STENCIL_APP_DATA_ID) {
return appData;
}
return null;
},
load(id) {
Expand All @@ -75,6 +84,14 @@ export const generateHydrateApp = async (
}
return null;
},
transform(code) {
/**
* Remove the modeResolutionChain variable from the generated code.
* This variable is redefined in `HYDRATE_FACTORY_INTRO` to ensure we can
* use it within the hydrate and global runtime.
*/
return code.replace(`var ${MODE_RESOLUTION_CHAIN_DECLARATION}`, '');
},
},
],
treeshake: false,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
export const HYDRATE_APP_CLOSURE_START = `/*hydrateAppClosure start*/`;

export const MODE_RESOLUTION_CHAIN_DECLARATION = `modeResolutionChain = [];`;

/**
* This is the entry point for the hydrate factory.
*
* __Note:__ the `modeResolutionChain` will be uncommented in the
* `src/compiler/output-targets/dist-hydrate-script/write-hydrate-outputs.ts` file. This enables us to use
* one module resolution chain across hydrate and core runtime.
*/
export const HYDRATE_FACTORY_INTRO = `
// const ${MODE_RESOLUTION_CHAIN_DECLARATION}
export function hydrateFactory($stencilWindow, $stencilHydrateOpts, $stencilHydrateResults, $stencilAfterHydrate, $stencilHydrateResolve) {
var globalThis = $stencilWindow;
var self = $stencilWindow;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { basename } from 'path';
import type { RollupOutput } from 'rollup';

import type * as d from '../../../declarations';
import { MODE_RESOLUTION_CHAIN_DECLARATION } from './hydrate-factory-closure';
import { relocateHydrateContextConst } from './relocate-hydrate-context';

export const writeHydrateOutputs = (
Expand Down Expand Up @@ -58,6 +59,15 @@ const writeHydrateOutput = async (
rollupOutput.output.map(async (output) => {
if (output.type === 'chunk') {
output.code = relocateHydrateContextConst(config, compilerCtx, output.code);

/**
* Enable the line where we define `modeResolutionChain` for the hydrate module.
*/
output.code = output.code.replace(
`// const ${MODE_RESOLUTION_CHAIN_DECLARATION}`,
`const ${MODE_RESOLUTION_CHAIN_DECLARATION}`,
);

const filePath = join(hydrateAppDirPath, output.fileName);
await compilerCtx.fs.writeFile(filePath, output.code, { immediateWrite: true });
}
Expand Down
6 changes: 6 additions & 0 deletions src/declarations/stencil-public-compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { ConfigFlags } from '../cli/config-flags';
import type { PrerenderUrlResults, PrintLine } from '../internal';
import type { BuildCtx, CompilerCtx } from './stencil-private';
import type { JsonDocs } from './stencil-public-docs';
import type { ResolutionHandler } from './stencil-public-runtime';

export * from './stencil-public-docs';

Expand Down Expand Up @@ -955,6 +956,11 @@ export interface SerializeDocumentOptions extends HydrateDocumentOptions {
* @default true
*/
fullDocument?: boolean;
/**
* Style modes to render the component in.
* @see https://stenciljs.com/docs/styling#style-modes
*/
modes?: ResolutionHandler[];
}

export interface HydrateFactoryOptions extends SerializeDocumentOptions {
Expand Down
13 changes: 12 additions & 1 deletion src/hydrate/runner/render.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Readable } from 'node:stream';

import { hydrateFactory } from '@hydrate-factory';
import { modeResolutionChain, setMode } from '@platform';
import { MockWindow, serializeNodeToHtml } from '@stencil/core/mock-doc';
import { hasError } from '@utils';

Expand Down Expand Up @@ -133,7 +134,17 @@ async function render(win: MockWindow, opts: HydrateFactoryOptions, results: Hyd
const beforeHydrateFn = typeof opts.beforeHydrate === 'function' ? opts.beforeHydrate : NOOP;
try {
await Promise.resolve(beforeHydrateFn(win.document));
return new Promise<HydrateResults>((resolve) => hydrateFactory(win, opts, results, afterHydrate, resolve));
return new Promise<HydrateResults>((resolve) => {
if (Array.isArray(opts.modes)) {
/**
* Reset the mode resolution chain as we expect every `renderToString` call to render
* the components in new environment/document.
*/
modeResolutionChain.length = 0;
opts.modes.forEach((mode) => setMode(mode));
}
return hydrateFactory(win, opts, results, afterHydrate, resolve);
});
} catch (e) {
renderCatchError(results, e);
return finalizeHydrate(win, win.document, opts, results);
Expand Down
29 changes: 29 additions & 0 deletions test/end-to-end/src/declarative-shadow-dom/test.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,4 +290,33 @@ describe('renderToString', () => {
});
expect(html).toBe('<cmp-with-slot custom-hydrate-flag="" s-id="1"><!--r.1-->Hello World</cmp-with-slot>');
});

describe('modes in declarative shadow dom', () => {
it('renders components in ios mode', async () => {
const { html } = await renderToString('<prop-cmp first="Max" last="Mustermann"></prop-cmp>', {
fullDocument: false,
prettyHtml: true,
modes: [() => 'ios'],
});
expect(html).toContain('<style>');
expect(html).toContain(';color:white;');
const page = await newE2EPage({ html, url: 'https://stencil.com' });
const div = await page.find('>>>div');
const { color } = await div.getComputedStyle();
expect(color).toBe('rgb(255, 255, 255)');
});

it('renders components in md mode', async () => {
const { html } = await renderToString('<prop-cmp first="Max" last="Mustermann"></prop-cmp>', {
fullDocument: false,
prettyHtml: true,
modes: [() => 'md'],
});
expect(html).toContain(';color:black;');
const page = await newE2EPage({ html, url: 'https://stencil.com' });
const div = await page.find('>>>div');
const { color } = await div.getComputedStyle();
expect(color).toBe('rgb(0, 0, 0)');
});
});
});
2 changes: 1 addition & 1 deletion test/end-to-end/src/prop-cmp/prop-cmp.md.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
font-size: 24px;
font-weight: bold;
background: #047aff;
color: white;
color: black;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}

Expand Down
2 changes: 1 addition & 1 deletion test/end-to-end/src/prop-cmp/prop-cmp.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, Prop, h, Host } from '@stencil/core';
import { Component, h, Host, Prop } from '@stencil/core';
import { saveAs } from 'file-saver';

/**
Expand Down

0 comments on commit 15f3b26

Please sign in to comment.