@teambit/ui 0.0.553 → 0.0.557
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/compose.d.ts +5 -0
- package/dist/open-browser.d.ts +4 -0
- package/dist/render-lifecycle.d.ts +31 -0
- package/dist/ssr/request-browser.d.ts +35 -0
- package/dist/ssr/request-server.d.ts +3 -0
- package/dist/start-plugin.d.ts +7 -0
- package/dist/start.cmd.d.ts +8 -1
- package/dist/ui-build.cmd.d.ts +8 -1
- package/dist/ui-root.d.ts +24 -0
- package/dist/ui-server.d.ts +14 -0
- package/dist/ui.main.runtime.d.ts +141 -1
- package/dist/ui.ui.runtime.d.ts +31 -1
- package/dist/webpack/html.d.ts +1 -0
- package/package.json +32 -24
- package/tsconfig.json +1 -2
- package/compose.tsx +0 -27
- package/create-root.ts +0 -94
- package/events/index.ts +0 -1
- package/events/ui-server-started-event.ts +0 -14
- package/exceptions/index.ts +0 -2
- package/exceptions/unknown-build-error.ts +0 -5
- package/exceptions/unknown-ui.ts +0 -9
- package/index.ts +0 -18
- package/open-browser.ts +0 -128
- package/package-tar/teambit-ui-0.0.553.tgz +0 -0
- package/render-lifecycle.tsx +0 -56
- package/ssr/render-middleware.ts +0 -129
- package/ssr/request-browser.ts +0 -86
- package/ssr/request-server.ts +0 -10
- package/ssr/ssr-content.ts +0 -9
- package/start-plugin.ts +0 -25
- package/start.cmd.tsx +0 -93
- package/types/asset.d.ts +0 -29
- package/types/style.d.ts +0 -42
- package/ui/client-context.module.scss +0 -4
- package/ui/client-context.tsx +0 -28
- package/ui-build.cmd.tsx +0 -27
- package/ui-root.tsx +0 -59
- package/ui-root.ui.ts +0 -7
- package/ui-server.ts +0 -249
- package/ui.aspect.ts +0 -10
- package/ui.cli.rt.tsx +0 -10
- package/ui.docs.md +0 -118
- package/ui.main.runtime.ts +0 -573
- package/ui.route.ts +0 -0
- package/ui.runtime.ts +0 -21
- package/ui.ui.runtime.tsx +0 -277
- package/webpack/html.ts +0 -24
- package/webpack/postcss.config.ts +0 -22
- package/webpack/webpack.base.config.ts +0 -392
- package/webpack/webpack.browser.config.ts +0 -125
- package/webpack/webpack.dev.config.ts +0 -336
- package/webpack/webpack.ssr.config.ts +0 -38
package/compose.tsx
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import React, { ReactNode, ComponentType } from 'react';
|
|
2
|
-
|
|
3
|
-
type ComponentTuple<T = any> = [Component: ComponentType<T>, props?: T];
|
|
4
|
-
export type Wrapper<T = any> = ComponentType<T> | ComponentTuple<T>;
|
|
5
|
-
interface Props<T = any> {
|
|
6
|
-
/** Compose these components. Can be a ReactComponent, or a [ReactComponent, Props] tuple */
|
|
7
|
-
components: Wrapper<T>[];
|
|
8
|
-
children?: ReactNode;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* A react Component composer. equivalent to `(n+1) => <a[n+1]> <Compose(a[n]) /> </a[n+1]>`.
|
|
13
|
-
* Component can be a React Component, or a `[Component, { ...props }]` tuple.
|
|
14
|
-
*/
|
|
15
|
-
export function Compose(props: Props) {
|
|
16
|
-
const { components = [], children } = props;
|
|
17
|
-
|
|
18
|
-
const arrayified: ComponentTuple[] = components.map((tuple) => (Array.isArray(tuple) ? tuple : [tuple, undefined]));
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<>
|
|
22
|
-
{arrayified.reduceRight((acc, [Comp, forwardProps]) => {
|
|
23
|
-
return <Comp {...forwardProps}>{acc}</Comp>;
|
|
24
|
-
}, children)}
|
|
25
|
-
</>
|
|
26
|
-
);
|
|
27
|
-
}
|
package/create-root.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { AspectDefinition } from '@teambit/aspect-loader';
|
|
2
|
-
import { toWindowsCompatiblePath } from '@teambit/toolbox.path.to-windows-compatible-path';
|
|
3
|
-
import { camelCase } from 'lodash';
|
|
4
|
-
import { parse } from 'path';
|
|
5
|
-
|
|
6
|
-
import { UIAspect } from './ui.aspect';
|
|
7
|
-
|
|
8
|
-
export async function createRoot(
|
|
9
|
-
aspectDefs: AspectDefinition[],
|
|
10
|
-
rootExtensionName?: string,
|
|
11
|
-
rootAspect = UIAspect.id,
|
|
12
|
-
runtime = 'ui',
|
|
13
|
-
config = {}
|
|
14
|
-
) {
|
|
15
|
-
const rootId = rootExtensionName ? `'${rootExtensionName}'` : '';
|
|
16
|
-
const identifiers = getIdentifiers(aspectDefs, 'Aspect');
|
|
17
|
-
|
|
18
|
-
const idSetters = getIdSetters(aspectDefs, 'Aspect');
|
|
19
|
-
|
|
20
|
-
return `
|
|
21
|
-
${createImports(aspectDefs)}
|
|
22
|
-
|
|
23
|
-
const isBrowser = typeof window !== "undefined";
|
|
24
|
-
const config = JSON.parse('${toWindowsCompatiblePath(JSON.stringify(config))}');
|
|
25
|
-
${idSetters.join('\n')}
|
|
26
|
-
|
|
27
|
-
export function render(...props){
|
|
28
|
-
return Harmony.load([${identifiers.join(', ')}], '${runtime}', config)
|
|
29
|
-
.then((harmony) => {
|
|
30
|
-
return harmony
|
|
31
|
-
.run()
|
|
32
|
-
.then(() => {
|
|
33
|
-
const rootExtension = harmony.get('${rootAspect}');
|
|
34
|
-
|
|
35
|
-
if (isBrowser) {
|
|
36
|
-
return rootExtension.render(${rootId}, ...props);
|
|
37
|
-
} else {
|
|
38
|
-
return rootExtension.renderSsr(${rootId}, ...props);
|
|
39
|
-
}
|
|
40
|
-
})
|
|
41
|
-
.catch((err) => {
|
|
42
|
-
throw err;
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (isBrowser) render();
|
|
48
|
-
`;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function createImports(aspectDefs: AspectDefinition[]) {
|
|
52
|
-
const defs = aspectDefs.filter((def) => def.runtimePath);
|
|
53
|
-
|
|
54
|
-
return `import { Harmony } from '@teambit/harmony';
|
|
55
|
-
${getImportStatements(aspectDefs, 'aspectPath', 'Aspect')}
|
|
56
|
-
${getImportStatements(defs, 'runtimePath', 'Runtime')}`;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function getImportStatements(aspectDefs: AspectDefinition[], pathProp: string, suffix: string): string {
|
|
60
|
-
return aspectDefs
|
|
61
|
-
.map(
|
|
62
|
-
(aspectDef) =>
|
|
63
|
-
`import ${getIdentifier(aspectDef, suffix)} from '${toWindowsCompatiblePath(aspectDef[pathProp])}';`
|
|
64
|
-
)
|
|
65
|
-
.join('\n');
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function getIdentifiers(aspectDefs: AspectDefinition[], suffix: string): string[] {
|
|
69
|
-
return aspectDefs.map((aspectDef) => `${getIdentifier(aspectDef, suffix)}`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function getIdSetters(defs: AspectDefinition[], suffix: string) {
|
|
73
|
-
return defs
|
|
74
|
-
.map((def) => {
|
|
75
|
-
if (!def.getId) return undefined;
|
|
76
|
-
return `${getIdentifier(def, suffix)}.id = '${def.getId}';`;
|
|
77
|
-
})
|
|
78
|
-
.filter((val) => !!val);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function getIdentifier(aspectDef: AspectDefinition, suffix: string): string {
|
|
82
|
-
if (!aspectDef.component && !aspectDef.local) {
|
|
83
|
-
return getCoreIdentifier(aspectDef.aspectPath, suffix);
|
|
84
|
-
}
|
|
85
|
-
return getRegularAspectIdentifier(aspectDef, suffix);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function getRegularAspectIdentifier(aspectDef: AspectDefinition, suffix: string): string {
|
|
89
|
-
return camelCase(`${parse(aspectDef.aspectPath).base.replace(/\./, '__').replace('@', '__')}${suffix}`);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function getCoreIdentifier(path: string, suffix: string): string {
|
|
93
|
-
return camelCase(`${parse(path).name.split('.')[0]}${suffix}`);
|
|
94
|
-
}
|
package/events/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './ui-server-started-event';
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/* eslint-disable max-classes-per-file */
|
|
2
|
-
import { BitBaseEvent } from '@teambit/pubsub';
|
|
3
|
-
|
|
4
|
-
class UiServerStartedEventData {
|
|
5
|
-
constructor(readonly targetHost, readonly targetPort, readonly uiRoot) {}
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export class UiServerStartedEvent extends BitBaseEvent<UiServerStartedEventData> {
|
|
9
|
-
static readonly TYPE = 'ui-server-started';
|
|
10
|
-
|
|
11
|
-
constructor(readonly timestamp, readonly targetHost, readonly targetPort, readonly uiRoot) {
|
|
12
|
-
super(UiServerStartedEvent.TYPE, '0.0.1', timestamp, new UiServerStartedEventData(targetHost, targetPort, uiRoot));
|
|
13
|
-
}
|
|
14
|
-
}
|
package/exceptions/index.ts
DELETED
package/exceptions/unknown-ui.ts
DELETED
package/index.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { UIAspect, UIRuntime } from './ui.aspect';
|
|
2
|
-
|
|
3
|
-
export * from './events';
|
|
4
|
-
export { UIRoot, PostStartOptions, ProxyEntry } from './ui-root';
|
|
5
|
-
export type { UiMain, PreStartOpts } from './ui.main.runtime';
|
|
6
|
-
export type { UiUI, ContextProps } from './ui.ui.runtime';
|
|
7
|
-
export { StartPlugin, StartPluginOptions } from './start-plugin';
|
|
8
|
-
export type { RenderLifecycle } from './render-lifecycle';
|
|
9
|
-
export { UIRootUI, UIRootFactory } from './ui-root.ui';
|
|
10
|
-
export type { UIServer } from './ui-server';
|
|
11
|
-
export { UIAspect, UIRuntime };
|
|
12
|
-
export type { BrowserData } from './ssr/request-browser';
|
|
13
|
-
export default UIAspect;
|
|
14
|
-
|
|
15
|
-
// using `useDataQuery` from this package is deprecated, use `@teambit/ui-foundation.ui.hooks.use-data-query` directly
|
|
16
|
-
export { DataQueryResult, useDataQuery } from '@teambit/ui-foundation.ui.hooks.use-data-query';
|
|
17
|
-
// temporary. TODO: fix this
|
|
18
|
-
export { useMutation } from '@apollo/client';
|
package/open-browser.ts
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import { execSync } from 'child_process';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import spawn from 'cross-spawn';
|
|
4
|
-
import open from 'open';
|
|
5
|
-
import { Logger } from '@teambit/logger';
|
|
6
|
-
|
|
7
|
-
export class OpenBrowser {
|
|
8
|
-
constructor(private logger: Logger) {}
|
|
9
|
-
OSX_CHROME = 'google chrome';
|
|
10
|
-
|
|
11
|
-
Actions = Object.freeze({
|
|
12
|
-
NONE: 0,
|
|
13
|
-
BROWSER: 1,
|
|
14
|
-
SCRIPT: 2,
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Reads the BROWSER environment variable and decides what to do with it. Returns
|
|
19
|
-
* true if it opened a browser or ran a node.js script, otherwise false.
|
|
20
|
-
*/
|
|
21
|
-
open(url: string) {
|
|
22
|
-
const { action, value, args } = this.getBrowserEnv();
|
|
23
|
-
switch (action) {
|
|
24
|
-
case this.Actions.NONE:
|
|
25
|
-
// Special case: BROWSER="none" will prevent opening completely.
|
|
26
|
-
return false;
|
|
27
|
-
case this.Actions.SCRIPT:
|
|
28
|
-
return this.executeNodeScript(value, url);
|
|
29
|
-
case this.Actions.BROWSER:
|
|
30
|
-
return this.startBrowserProcess(value, url, args);
|
|
31
|
-
default:
|
|
32
|
-
throw new Error('Not implemented.');
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
private getBrowserEnv() {
|
|
37
|
-
// Attempt to honor this environment variable.
|
|
38
|
-
// It is specific to the operating system.
|
|
39
|
-
// See https://github.com/sindresorhus/open#app for documentation.
|
|
40
|
-
const value = process.env.BROWSER;
|
|
41
|
-
const args = process.env.BROWSER_ARGS ? process.env.BROWSER_ARGS.split(' ') : [];
|
|
42
|
-
let action;
|
|
43
|
-
if (!value) {
|
|
44
|
-
// Default.
|
|
45
|
-
action = this.Actions.BROWSER;
|
|
46
|
-
} else if (value.toLowerCase().endsWith('.js')) {
|
|
47
|
-
action = this.Actions.SCRIPT;
|
|
48
|
-
} else if (value.toLowerCase() === 'none') {
|
|
49
|
-
action = this.Actions.NONE;
|
|
50
|
-
} else {
|
|
51
|
-
action = this.Actions.BROWSER;
|
|
52
|
-
}
|
|
53
|
-
return { action, value, args };
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
private executeNodeScript(scriptPath, url) {
|
|
57
|
-
const extraArgs = process.argv.slice(2);
|
|
58
|
-
const child = spawn(process.execPath, [scriptPath, ...extraArgs, url], {
|
|
59
|
-
stdio: 'inherit',
|
|
60
|
-
});
|
|
61
|
-
child.on('close', (code) => {
|
|
62
|
-
if (code !== 0) {
|
|
63
|
-
this.logger.info(chalk.red('The script specified as BROWSER environment variable failed.'));
|
|
64
|
-
this.logger.info(`${chalk.cyan(scriptPath)} exited with code ${code}.`);
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
return true;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
private startBrowserProcess(browser: any, url: string, args: any) {
|
|
71
|
-
// If we're on OS X, the user hasn't specifically
|
|
72
|
-
// requested a different browser, we can try opening
|
|
73
|
-
// Chrome with AppleScript. This lets us reuse an
|
|
74
|
-
// existing tab when possible instead of creating a new one.
|
|
75
|
-
const shouldTryOpenChromiumWithAppleScript =
|
|
76
|
-
process.platform === 'darwin' && (typeof browser !== 'string' || browser === this.OSX_CHROME);
|
|
77
|
-
|
|
78
|
-
if (shouldTryOpenChromiumWithAppleScript) {
|
|
79
|
-
// Will use the first open browser found from list
|
|
80
|
-
const supportedChromiumBrowsers = [
|
|
81
|
-
'Google Chrome Canary',
|
|
82
|
-
'Google Chrome',
|
|
83
|
-
'Microsoft Edge',
|
|
84
|
-
'Brave Browser',
|
|
85
|
-
'Vivaldi',
|
|
86
|
-
'Chromium',
|
|
87
|
-
];
|
|
88
|
-
|
|
89
|
-
for (const chromiumBrowser of supportedChromiumBrowsers) {
|
|
90
|
-
try {
|
|
91
|
-
// Try our best to reuse existing tab
|
|
92
|
-
// on OSX Chromium-based browser with AppleScript
|
|
93
|
-
execSync(`ps cax | grep "${chromiumBrowser}"`);
|
|
94
|
-
execSync(`osascript openChrome.applescript "${encodeURI(url)}" "${chromiumBrowser}"`, {
|
|
95
|
-
cwd: __dirname,
|
|
96
|
-
stdio: 'ignore',
|
|
97
|
-
});
|
|
98
|
-
return true;
|
|
99
|
-
} catch (err: any) {
|
|
100
|
-
// Ignore errors.
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Another special case: on OS X, check if BROWSER has been set to "open".
|
|
106
|
-
// In this case, instead of passing `open` to `opn` (which won't work),
|
|
107
|
-
// just ignore it (thus ensuring the intended behavior, i.e. opening the system browser):
|
|
108
|
-
// https://github.com/facebook/create-react-app/pull/1690#issuecomment-283518768
|
|
109
|
-
if (process.platform === 'darwin' && browser === 'open') {
|
|
110
|
-
browser = undefined;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// If there are arguments, they must be passed as array with the browser
|
|
114
|
-
if (typeof browser === 'string' && args.length > 0) {
|
|
115
|
-
browser = [browser].concat(args);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Fallback to open
|
|
119
|
-
// (It will always open new tab)
|
|
120
|
-
try {
|
|
121
|
-
const options = { app: browser, wait: false, url: true };
|
|
122
|
-
open(url, options).catch(() => {}); // Prevent `unhandledRejection` error.
|
|
123
|
-
return true;
|
|
124
|
-
} catch (err: any) {
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
Binary file
|
package/render-lifecycle.tsx
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { ReactNode, ComponentType } from 'react';
|
|
2
|
-
import { BrowserData } from './ssr/request-browser';
|
|
3
|
-
import { RequestServer } from './ssr/request-server';
|
|
4
|
-
import { ContextProps } from './ui.ui.runtime';
|
|
5
|
-
|
|
6
|
-
export type RenderLifecycle<RenderCtx = any, Serialized = any> = {
|
|
7
|
-
/**
|
|
8
|
-
* Initialize a context state for this specific rendering.
|
|
9
|
-
* Context state will only be available to the current Aspect, in the other hooks, as well as a prop to the react context component.
|
|
10
|
-
*/
|
|
11
|
-
serverInit?: (state: {
|
|
12
|
-
browser?: BrowserData;
|
|
13
|
-
server?: RequestServer;
|
|
14
|
-
}) => RenderCtx | void | undefined | Promise<RenderCtx | void | undefined>;
|
|
15
|
-
/**
|
|
16
|
-
* Executes before running ReactDOM.renderToString(). Return value will replace the existing context state.
|
|
17
|
-
*/
|
|
18
|
-
onBeforeRender?: (
|
|
19
|
-
ctx: RenderCtx,
|
|
20
|
-
app: ReactNode
|
|
21
|
-
) => RenderCtx | void | undefined | Promise<RenderCtx | void | undefined>;
|
|
22
|
-
/**
|
|
23
|
-
* Produce html assets. Runs after the body is rendered, and before rendering the final html.
|
|
24
|
-
* @returns
|
|
25
|
-
* json: will be rendered to the dom as a `<script type="json"/>`.
|
|
26
|
-
* More assets will be available in the future.
|
|
27
|
-
*/
|
|
28
|
-
serialize?: (ctx: RenderCtx, app: ReactNode) => { json: string } | Promise<{ json: string }> | undefined;
|
|
29
|
-
/**
|
|
30
|
-
* Converts serialized data from raw string back to structured data.
|
|
31
|
-
* @example deserialize: (data) => { const parsed = JSON.parse(data); return { analytics: new AnalyticsService(parsed); } }
|
|
32
|
-
*/
|
|
33
|
-
deserialize?: (data?: string) => Serialized;
|
|
34
|
-
/**
|
|
35
|
-
* Initialize the context state for client side rendering.
|
|
36
|
-
* Context state will only be available to the current Aspect, in the other hooks, as well as a prop to the react context component.
|
|
37
|
-
*/
|
|
38
|
-
browserInit?: (deserializedData: Serialized) => RenderCtx | void | undefined | Promise<RenderCtx | void | undefined>;
|
|
39
|
-
/**
|
|
40
|
-
* Executes before running ReactDOM.hydrate() (or .render() in case server side rendering is skipped). Receives the context produced by `deserialize()`
|
|
41
|
-
*/
|
|
42
|
-
onBeforeHydrate?: (
|
|
43
|
-
context: RenderCtx,
|
|
44
|
-
app: ReactNode
|
|
45
|
-
) => RenderCtx | void | undefined | Promise<RenderCtx | void | undefined>;
|
|
46
|
-
/**
|
|
47
|
-
* Executes after browser rendering is complete. Receives context from the previous steps.
|
|
48
|
-
* @example onHydrate: (ref, { analytics }) => { analytics.reportPageView() }
|
|
49
|
-
*/
|
|
50
|
-
onHydrate?: (context: RenderCtx, ref: HTMLElement | null) => void;
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Wraps dom with a context. Will receive render context, produced by `onBeforeRender()` (at server-side) or `deserialize()` (at the browser)
|
|
54
|
-
*/
|
|
55
|
-
reactContext?: ComponentType<ContextProps<RenderCtx>>;
|
|
56
|
-
};
|
package/ssr/render-middleware.ts
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import type { Assets } from '@teambit/ui-foundation.ui.rendering.html';
|
|
2
|
-
import { Request, Response, NextFunction } from 'express';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import * as fs from 'fs-extra';
|
|
5
|
-
import type { Logger } from '@teambit/logger';
|
|
6
|
-
import { requestToObj } from './request-browser';
|
|
7
|
-
import { SsrContent } from './ssr-content';
|
|
8
|
-
|
|
9
|
-
const denyList = /^\/favicon.ico$/;
|
|
10
|
-
|
|
11
|
-
type ssrRenderProps = {
|
|
12
|
-
root: string;
|
|
13
|
-
port: number;
|
|
14
|
-
title: string;
|
|
15
|
-
logger: Logger;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
type ManifestFile = {
|
|
19
|
-
files?: Record<string, string>;
|
|
20
|
-
entrypoints?: string[];
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export async function createSsrMiddleware({ root, port, title, logger }: ssrRenderProps) {
|
|
24
|
-
const runtime = await loadRuntime(root, { logger });
|
|
25
|
-
if (!runtime) return undefined;
|
|
26
|
-
|
|
27
|
-
const { render } = runtime;
|
|
28
|
-
const assets = { ...runtime.assets, title };
|
|
29
|
-
|
|
30
|
-
return async function serverRenderMiddleware(req: Request, res: Response, next: NextFunction) {
|
|
31
|
-
const { query, url } = req;
|
|
32
|
-
|
|
33
|
-
const browser = requestToObj(req, port);
|
|
34
|
-
|
|
35
|
-
if (denyList.test(url)) {
|
|
36
|
-
logger.debug(`[ssr] skipping static denyList file ${url}`);
|
|
37
|
-
next();
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (query.rendering === 'client') {
|
|
42
|
-
logger.debug(`[ssr] skipping ${url}`);
|
|
43
|
-
next();
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
logger.debug(`[ssr] ${req.method} ${url}`);
|
|
48
|
-
const server = { port, request: req, response: res };
|
|
49
|
-
const props: SsrContent = { assets, browser, server };
|
|
50
|
-
|
|
51
|
-
try {
|
|
52
|
-
const rendered = await render(props);
|
|
53
|
-
res.set('Cache-Control', 'no-cache');
|
|
54
|
-
res.send(rendered);
|
|
55
|
-
logger.debug(`[ssr] success '${url}'`);
|
|
56
|
-
} catch (e: any) {
|
|
57
|
-
logger.error(`[ssr] failed at '${url}'`, e);
|
|
58
|
-
next();
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async function loadRuntime(root: string, { logger }: { logger: Logger }) {
|
|
64
|
-
let render: (...arg: any[]) => any;
|
|
65
|
-
let assets: Assets | undefined;
|
|
66
|
-
|
|
67
|
-
try {
|
|
68
|
-
const entryFilepath = path.join(root, 'ssr', 'index.js');
|
|
69
|
-
if (!fs.existsSync(entryFilepath)) {
|
|
70
|
-
logger.warn(`[ssr] - Skipping setup - failed finding ssr bundle at "${entryFilepath}"`);
|
|
71
|
-
return undefined;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const manifestFilepath = path.join(root, 'asset-manifest.json');
|
|
75
|
-
if (!fs.existsSync(manifestFilepath)) {
|
|
76
|
-
logger.warn('[ssr] - Skipping setup (cannot find asset manifest file)');
|
|
77
|
-
return undefined;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
assets = await parseManifest(manifestFilepath);
|
|
81
|
-
if (!assets) {
|
|
82
|
-
logger.warn('[ssr] - Skipping setup (failed parsing assets manifest)');
|
|
83
|
-
return undefined;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const imported = await import(entryFilepath);
|
|
87
|
-
render = imported?.render;
|
|
88
|
-
|
|
89
|
-
if (!render || typeof render !== 'function') {
|
|
90
|
-
logger.warn('[ssr] - index file does not export a render() function. Skipping setup.');
|
|
91
|
-
return undefined;
|
|
92
|
-
}
|
|
93
|
-
} catch (e: any) {
|
|
94
|
-
logger.error(e);
|
|
95
|
-
return undefined;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return {
|
|
99
|
-
render,
|
|
100
|
-
assets,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async function parseManifest(filepath: string, logger?: Logger) {
|
|
105
|
-
try {
|
|
106
|
-
const file = await fs.readFile(filepath);
|
|
107
|
-
logger?.debug('[ssr] - ✓ aread manifest file');
|
|
108
|
-
const contents = file.toString();
|
|
109
|
-
const parsed: ManifestFile = JSON.parse(contents);
|
|
110
|
-
logger?.debug('[ssr] - ✓ prased manifest file', parsed);
|
|
111
|
-
const assets = getAssets(parsed);
|
|
112
|
-
logger?.debug('[ssr] - ✓ extracted data from manifest file', assets);
|
|
113
|
-
|
|
114
|
-
return assets;
|
|
115
|
-
} catch (e: any) {
|
|
116
|
-
logger?.error('[ssr] - error parsing asset manifest', e);
|
|
117
|
-
process.exit();
|
|
118
|
-
return undefined;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function getAssets(manifest: ManifestFile) {
|
|
123
|
-
const assets: Assets = { css: [], js: [] };
|
|
124
|
-
|
|
125
|
-
assets.css = manifest.entrypoints?.filter((x) => x.endsWith('css')).map((x) => path.join('/', x));
|
|
126
|
-
assets.js = manifest.entrypoints?.filter((x) => x.endsWith('js')).map((x) => path.join('/', x));
|
|
127
|
-
|
|
128
|
-
return assets;
|
|
129
|
-
}
|
package/ssr/request-browser.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import type { IncomingHttpHeaders } from 'http';
|
|
2
|
-
import type { Request } from 'express';
|
|
3
|
-
|
|
4
|
-
export type ParsedQuery = { [key: string]: undefined | string | string[] | ParsedQuery | ParsedQuery[] };
|
|
5
|
-
|
|
6
|
-
export type BrowserData = {
|
|
7
|
-
connection: {
|
|
8
|
-
secure: boolean;
|
|
9
|
-
headers: IncomingHttpHeaders;
|
|
10
|
-
body: any;
|
|
11
|
-
};
|
|
12
|
-
/**
|
|
13
|
-
* isomorphic location object, resembling the browser's window.location
|
|
14
|
-
*/
|
|
15
|
-
location: {
|
|
16
|
-
/** hostname + port
|
|
17
|
-
* @example localhost:3000
|
|
18
|
-
*/
|
|
19
|
-
host: string;
|
|
20
|
-
/**
|
|
21
|
-
* @example localhost
|
|
22
|
-
*/
|
|
23
|
-
hostname: string;
|
|
24
|
-
/** full url
|
|
25
|
-
* @example http://localhost:3000/components?q=button
|
|
26
|
-
*/
|
|
27
|
-
href: string;
|
|
28
|
-
/** full url without query
|
|
29
|
-
* @example http://localhost:3000/components
|
|
30
|
-
*/
|
|
31
|
-
origin: string;
|
|
32
|
-
/**
|
|
33
|
-
* @example /component
|
|
34
|
-
*/
|
|
35
|
-
pathname: string;
|
|
36
|
-
/**
|
|
37
|
-
* @example 3000
|
|
38
|
-
*/
|
|
39
|
-
port: number;
|
|
40
|
-
/**
|
|
41
|
-
* @example http
|
|
42
|
-
*/
|
|
43
|
-
protocol: string;
|
|
44
|
-
/**
|
|
45
|
-
* parsed search params
|
|
46
|
-
* @example { one: 1, two: [2,3]}
|
|
47
|
-
*/
|
|
48
|
-
query: ParsedQuery;
|
|
49
|
-
/**
|
|
50
|
-
* full resource path, including query, without hostname
|
|
51
|
-
* @example /components?q=button
|
|
52
|
-
*/
|
|
53
|
-
url: string;
|
|
54
|
-
};
|
|
55
|
-
cookie?: string;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* extract relevant information from Express request.
|
|
60
|
-
*/
|
|
61
|
-
export function requestToObj(req: Request, port: number) {
|
|
62
|
-
// apparently port is not readily available in request.
|
|
63
|
-
|
|
64
|
-
const browser: BrowserData = {
|
|
65
|
-
connection: {
|
|
66
|
-
secure: req.secure,
|
|
67
|
-
headers: req.headers,
|
|
68
|
-
body: req.body,
|
|
69
|
-
},
|
|
70
|
-
// trying to match to browser.location
|
|
71
|
-
location: {
|
|
72
|
-
host: `${req.hostname}:${port}`,
|
|
73
|
-
hostname: req.hostname,
|
|
74
|
-
href: `${req.protocol}://${req.hostname}:${port}${req.url}`,
|
|
75
|
-
origin: `${req.protocol}://${req.hostname}:${port}`,
|
|
76
|
-
pathname: req.path,
|
|
77
|
-
port,
|
|
78
|
-
protocol: `${req.protocol}:`,
|
|
79
|
-
query: req.query,
|
|
80
|
-
url: req.url,
|
|
81
|
-
},
|
|
82
|
-
cookie: req.header('Cookie'),
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
return browser;
|
|
86
|
-
}
|
package/ssr/request-server.ts
DELETED
package/ssr/ssr-content.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { Assets } from '@teambit/ui-foundation.ui.rendering.html';
|
|
2
|
-
import { BrowserData } from './request-browser';
|
|
3
|
-
import { RequestServer } from './request-server';
|
|
4
|
-
|
|
5
|
-
export type SsrContent = {
|
|
6
|
-
assets?: Assets;
|
|
7
|
-
browser?: BrowserData;
|
|
8
|
-
server?: RequestServer;
|
|
9
|
-
};
|
package/start-plugin.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { ComponentType } from 'react';
|
|
2
|
-
import { ProxyEntry } from './ui-root';
|
|
3
|
-
|
|
4
|
-
export type StartPluginOptions = {
|
|
5
|
-
/**
|
|
6
|
-
* indicates whether the start in on verbose mode.
|
|
7
|
-
*/
|
|
8
|
-
verbose?: boolean;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* component pattern it applies on.
|
|
12
|
-
*/
|
|
13
|
-
pattern?: string;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export interface StartPlugin {
|
|
17
|
-
initiate(startOptions: StartPluginOptions): void;
|
|
18
|
-
|
|
19
|
-
getProxy?(): ProxyEntry[];
|
|
20
|
-
|
|
21
|
-
render: ComponentType;
|
|
22
|
-
|
|
23
|
-
/** promise that resolves when the plugin completed initiation */
|
|
24
|
-
readonly whenReady: Promise<void>;
|
|
25
|
-
}
|