Skip to content

Commit

Permalink
fix(hydrate): improve hydrate app builds
Browse files Browse the repository at this point in the history
  • Loading branch information
adamdbradley committed Jun 28, 2019
1 parent 9f83b54 commit 3267f66
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 48 deletions.
4 changes: 2 additions & 2 deletions src/compiler/component-hydrate/generate-hydrate-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import * as d from '../../declarations';
import { bundleHydrateApp } from './bundle-hydrate-app';
import { DEFAULT_STYLE_MODE, catchError } from '@utils';
import { getBuildFeatures, updateBuildConditionals } from '../app-core/build-conditionals';
import { getHydrateAppFileName, writeHydrateOutputs } from './write-hydrate-outputs';
import { updateToHydrateComponents } from './update-to-hydrate-components';
import { writeHydrateOutputs } from './write-hydrate-outputs';


export async function generateHydrateApp(config: d.Config, compilerCtx: d.CompilerCtx, buildCtx: d.BuildCtx, outputTargets: d.OutputTargetHydrate[]) {
Expand All @@ -17,7 +17,7 @@ export async function generateHydrateApp(config: d.Config, compilerCtx: d.Compil
if (rollupAppBuild != null) {
const rollupOutput = await rollupAppBuild.generate({
format: 'cjs',
file: 'app.js',
file: getHydrateAppFileName(config),
chunkFileNames: '[name].js',
});

Expand Down
47 changes: 31 additions & 16 deletions src/compiler/component-hydrate/write-hydrate-outputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,19 @@ export function writeHydrateOutputs(config: d.Config, compilerCtx: d.CompilerCtx


async function writeHydrateOutput(config: d.Config, compilerCtx: d.CompilerCtx, buildCtx: d.BuildCtx, outputTarget: d.OutputTargetHydrate, rollupOutput: RollupOutput) {
const hydrateAppFileName = getHydrateAppFileName(config);
const hydratePackageName = await getHydratePackageName(config, compilerCtx);

const hydrateAppDirPath = outputTarget.dir;

const hydrateCoreIndexPath = config.sys.path.join(hydrateAppDirPath, 'index.js');
const hydrateCoreIndexDtsFilePath = config.sys.path.join(hydrateAppDirPath, 'index.d.ts');

const pkgJsonPath = config.sys.path.join(hydrateAppDirPath, 'package.json');
const pkgJsonCode = await getHydratePackageJson(config, compilerCtx, hydrateCoreIndexPath, hydrateCoreIndexDtsFilePath);
const pkgJsonCode = getHydratePackageJson(config, hydrateCoreIndexPath, hydrateCoreIndexDtsFilePath, hydratePackageName);

const writePromises: Promise<any>[] = [
copyHydrateRunner(config, compilerCtx, hydrateAppDirPath),
copyHydrateRunner(config, compilerCtx, hydrateAppDirPath, hydrateAppFileName, hydratePackageName),
compilerCtx.fs.writeFile(pkgJsonPath, pkgJsonCode)
];

Expand All @@ -35,28 +38,35 @@ async function writeHydrateOutput(config: d.Config, compilerCtx: d.CompilerCtx,
}


async function getHydratePackageJson(config: d.Config, compilerCtx: d.CompilerCtx, hydrateAppFilePath: string, hydrateDtsFilePath: string) {
let pkgName: string;
function getHydratePackageJson(config: d.Config, hydrateAppFilePath: string, hydrateDtsFilePath: string, hydratePackageName: string) {
const pkg: d.PackageJsonData = {
name: hydratePackageName,
description: `${config.namespace} component hydration app built for a NodeJS environment.`,
main: config.sys.path.basename(hydrateAppFilePath),
types: config.sys.path.basename(hydrateDtsFilePath)
};
return JSON.stringify(pkg, null, 2);
}


async function getHydratePackageName(config: d.Config, compilerCtx: d.CompilerCtx) {
try {
const rootPkgFilePath = config.sys.path.join(config.rootDir, 'package.json');
const pkgStr = await compilerCtx.fs.readFile(rootPkgFilePath);
const pkgData = JSON.parse(pkgStr) as d.PackageJsonData;
pkgName = `${pkgData.name}/hydrate`;
return `${pkgData.name}/hydrate`;
} catch (e) {}

} catch (e) {
pkgName = `${config.fsNamespace}/hydrate`;
}
return `${config.fsNamespace}/hydrate`;
}

const pkg: d.PackageJsonData = {
name: pkgName,
main: config.sys.path.basename(hydrateAppFilePath),
types: config.sys.path.basename(hydrateDtsFilePath)
};
return JSON.stringify(pkg, null, 2);

export function getHydrateAppFileName(config: d.Config) {
return `${config.fsNamespace}-hydrate.js`;
}


async function copyHydrateRunner(config: d.Config, compilerCtx: d.CompilerCtx, hydrateAppDirPath: string) {
async function copyHydrateRunner(config: d.Config, compilerCtx: d.CompilerCtx, hydrateAppDirPath: string, hydrateAppFileName: string, hydratePackageName: string) {
const srcHydrateDir = config.sys.path.join(config.sys.compiler.distDir, 'hydrate');

const runnerIndexFileName = 'index.js';
Expand All @@ -68,8 +78,13 @@ async function copyHydrateRunner(config: d.Config, compilerCtx: d.CompilerCtx, h
const runnerDestPath = config.sys.path.join(hydrateAppDirPath, runnerIndexFileName);
const runnerDtsDestPath = config.sys.path.join(hydrateAppDirPath, runnerDtsFileName);

let runnerSrcCode = await compilerCtx.fs.readFile(runnerSrcPath);

runnerSrcCode = runnerSrcCode.replace('$$HYDRATE_APP_FILENAME$$', hydrateAppFileName);
runnerSrcCode = runnerSrcCode.replace('$$HYDRATE_APP_PACKAGE_NAME$$', hydratePackageName);

await Promise.all([
compilerCtx.fs.copyFile(runnerSrcPath, runnerDestPath),
compilerCtx.fs.writeFile(runnerDestPath, runnerSrcCode),
compilerCtx.fs.copyFile(runnerDtsSrcPath, runnerDtsDestPath)
]);
}
1 change: 1 addition & 0 deletions src/declarations/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export interface PackageJsonData {
name?: string;
version?: string;
main?: string;
description?: string;
bin?: {[key: string]: string};
browser?: string;
module?: string;
Expand Down
3 changes: 3 additions & 0 deletions src/hydrate/platform/bootstrap-hydrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ function connectElements(win: Window, opts: d.HydrateDocumentOptions, results: B


function shouldHydrate(elm: Element): boolean {
if (elm.nodeType === 9) {
return true;
}
if (NO_HYDRATE_TAGS.has(elm.nodeName)) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
import { MockWindow, patchWindow } from '@mock-doc';


export function polyfillDocumentImplementation(win: any, doc: any) {
export function patchDomImplementation(doc: any) {
let win: any;

if (doc.defaultView != null) {
patchWindow(doc.defaultView);
win = doc.defaultView;

} else {
win = new MockWindow(false) as any;
}

if (win.document !== doc) {
win.document = doc;
}

if (doc.defaultView !== win) {
doc.defaultView = win;
}

const HTMLElement = doc.documentElement.constructor.prototype;
if (typeof HTMLElement.getRootNode !== 'function') {
const elm = doc.createElement('unknown-element');
Expand All @@ -14,6 +33,8 @@ export function polyfillDocumentImplementation(win: any, doc: any) {
win.CustomEvent = CustomEvent;
}
}

return win as Window;
}


Expand Down
14 changes: 3 additions & 11 deletions src/hydrate/runner/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { generateHydrateResults, normalizeHydrateOptions, renderBuildError, rend
import { initializeWindow } from './window-initialize';
import { inspectElement } from './inspect-document';
import { MockWindow, serializeNodeToHtml } from '@mock-doc';
import { polyfillDocumentImplementation } from './polyfill-implementation';
import { patchDomImplementation } from './patch-dom-implementation';


export async function renderToString(html: string, opts: d.RenderToStringOptions = {}) {
Expand Down Expand Up @@ -85,17 +85,9 @@ export async function hydrateDocument(doc: Document, opts: d.HydrateDocumentOpti
}

try {
const win: Window = doc.defaultView || new MockWindow(false) as any;
if (win.document !== doc) {
(win as any).document = doc;
}
if (doc.defaultView !== win) {
(doc as any).defaultView = win;
}
const win = patchDomImplementation(doc);

polyfillDocumentImplementation(win, doc);

await render(win, doc, opts, results);
await render(win, win.document, opts, results);

} catch (e) {
renderCatchError(results, e);
Expand Down
26 changes: 21 additions & 5 deletions src/hydrate/runner/vm-sandbox.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require;

const hydrateAppFileName = '$$HYDRATE_APP_FILENAME$$';
const hydrateAppPackageName = '$$HYDRATE_APP_PACKAGE_NAME$$';


const requireFunc: any = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require;
const vm = requireFunc('vm');


Expand All @@ -19,12 +24,23 @@ function loadHydrateAppScript() {
if (cachedAppScript == null) {
const fs = requireFunc('fs');
const path = requireFunc('path');
const filePath = path.join(__dirname, 'app.js');
const appCode = fs.readFileSync(filePath, 'utf8');

const code = `StencilHydrateApp = (exports => {${appCode};return exports; })({});`;
let hydrateAppFilePath: string;
let hydrateAppCode: string;
try {
hydrateAppFilePath = path.join(__dirname, hydrateAppFileName);
hydrateAppCode = fs.readFileSync(hydrateAppFilePath, 'utf8');

} catch (e) {
const hydrateAppPackageIndex = requireFunc.resolve(hydrateAppPackageName);
const hydrateAppPackageDir = path.dirname(hydrateAppPackageIndex);
hydrateAppFilePath = path.join(hydrateAppPackageDir, hydrateAppFileName);
hydrateAppCode = fs.readFileSync(hydrateAppFilePath, 'utf8');
}

const code = `StencilHydrateApp = (exports => {${hydrateAppCode};return exports; })({});`;

cachedAppScript = new vm.Script(code, { filename: filePath });
cachedAppScript = new vm.Script(code, { filename: hydrateAppFilePath });
}

return cachedAppScript;
Expand Down
57 changes: 45 additions & 12 deletions src/mock-doc/global.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,60 @@
import { MockWindow, resetWindow } from './window';


export function setupGlobal(global: any) {
if (global.window == null) {
const win: any = global.window = new MockWindow();
export function setupGlobal(gbl: any) {
if (gbl.window == null) {
const win: any = gbl.window = new MockWindow();

WINDOW_FUNCTIONS.forEach(fnName => {
if (!(fnName in global)) {
global[fnName] = win[fnName].bind(win);
if (!(fnName in gbl)) {
gbl[fnName] = win[fnName].bind(win);
}
});

WINDOW_PROPS.forEach(propName => {
if (!(propName in global)) {
Object.defineProperty(global, propName, {
if (!(propName in gbl)) {
Object.defineProperty(gbl, propName, {
get() { return win[propName]; },
set(val: any) { win[propName] = val; },
configurable: true,
enumerable: true
});

}
});
}

return global.window;
return gbl.window;
}


export function teardownGlobal(global: any) {
const win = global.window as Window;
export function teardownGlobal(gbl: any) {
const win = gbl.window as Window;
resetWindow(win);
}


export function patchWindow(winToBePatched: any) {
const mockWin: any = new MockWindow(false);

WINDOW_FUNCTIONS.forEach(fnName => {
if (typeof winToBePatched[fnName] !== 'function') {
winToBePatched[fnName] = mockWin[fnName].bind(mockWin);
}
});

WINDOW_PROPS.forEach(propName => {
if (winToBePatched === undefined) {
Object.defineProperty(winToBePatched, propName, {
get() { return mockWin[propName]; },
set(val: any) { mockWin[propName] = val; },
configurable: true,
enumerable: true
});
}
});
}


const WINDOW_FUNCTIONS = [
'addEventListener',
'cancelAnimationFrame',
Expand All @@ -42,18 +63,30 @@ const WINDOW_FUNCTIONS = [
'matchMedia',
'removeEventListener',
'requestAnimationFrame',
'requestIdleCallback'
'requestIdleCallback',
'URL'
];


const WINDOW_PROPS = [
'customElements',
'devicePixelRatio',
'document',
'history',
'innerHeight',
'innerWidth',
'localStorage',
'location',
'navigator',
'pageXOffset',
'pageYOffset',
'performance',
'screenLeft',
'screenTop',
'screenX',
'screenY',
'scrollX',
'scrollY',
'sessionStorage',
'CSS',
'CustomEvent',
Expand Down
2 changes: 1 addition & 1 deletion src/mock-doc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ export { MockDocument, createDocument, createFragment, resetDocument } from './d
export { MockWindow, cloneDocument, cloneWindow, constrainTimeouts, resetWindow } from './window';
export { NODE_TYPES } from './constants';
export { parseHtmlToDocument, parseHtmlToFragment} from './parse-html';
export { patchWindow, setupGlobal, teardownGlobal } from './global';
export { serializeNodeToHtml } from './serialize-node';
export { setupGlobal, teardownGlobal } from './global';

0 comments on commit 3267f66

Please sign in to comment.