Skip to content
This repository has been archived by the owner on Jul 23, 2019. It is now read-only.

Commit

Permalink
Add basic content api support
Browse files Browse the repository at this point in the history
Closes #12
  • Loading branch information
James Canning committed Jan 23, 2018
1 parent 3c4368e commit 9c0df37
Show file tree
Hide file tree
Showing 10 changed files with 290 additions and 135 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"license": "MIT",
"devDependencies": {
"@types/chokidar": "^1.7.4",
"@types/cookie-parser": "^1.4.1",
"@types/copy-paste": "^1.1.30",
"@types/express": "^4.11.0",
"@types/fs-extra": "^5.0.0",
Expand Down Expand Up @@ -42,6 +43,7 @@
"babel-register": "^6.24.1",
"chalk": "^2.3.0",
"chokidar": "^2.0.0",
"cookie-parser": "^1.4.3",
"copy-paste": "^1.3.0",
"express": "^4.15.3",
"fs-extra": "^5.0.0",
Expand Down
69 changes: 69 additions & 0 deletions src/content.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import fetch from 'node-fetch';
import { ContentApiOptions } from './types';
import chalk from 'chalk';

export function normaliseContentPath(path: string) {
if (path === '/') {
return path;
}

let fixedPath = path.endsWith('/') ? path.slice(0, -1) : path;
fixedPath = !fixedPath.startsWith('/') ? `/${fixedPath}` : fixedPath;

return fixedPath;
}

async function loadContentPathsFromApi(contentApiOptions: ContentApiOptions) {
const response = await fetch(contentApiOptions.endpoint, {
method: 'post',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
query: `query AllContentPages {
allPages {
path
}
}`,
}),
});

const data = await response.json();

console.log(
chalk`{keyword('orange') [content] {bold ${
data.data.allPages.length
}} page paths loaded from content api}`
);

return data.data.allPages
.map((page: { path: string | null }) => {
if (
!page.path ||
contentApiOptions.skipPaths.indexOf(page.path) >= 0
) {
return null;
}

return normaliseContentPath(page.path);
})
.filter((page: { path: string | null }) => page !== null);
}

export function createContentCache() {
const cache = { paths: null, data: null };
console.log(chalk`{keyword('orange') [content] content api enabled.}`);

return {
async getAllPaths(contentApiOptions: ContentApiOptions) {
if (cache.paths !== null) {
return cache.paths;
}

const paths = await loadContentPathsFromApi(contentApiOptions);
cache.paths = paths;

return paths;
},
};
}
25 changes: 22 additions & 3 deletions src/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import {
Compfile,
DirtyChangeset,
HydroleafMode,
PageComponentMap,
StateSnapshot,
} from './types';
import { createContentCache } from './content';

function differencesUI(differences: DirtyChangeset, next: StateSnapshot) {
// exit if nothing
Expand Down Expand Up @@ -47,7 +49,9 @@ function differencesUI(differences: DirtyChangeset, next: StateSnapshot) {
ncp.copy(content, () => {
if (type === 'template') {
console.log(
chalk`📋 {underline Template} {blue ${name}}: {green ${part !== null ? part : ''}}.
chalk`📋 {underline Template} {blue ${name}}: {green ${
part !== null ? part : ''
}}.
{italic Paste away!}`
);
} else if (type === 'page') {
Expand All @@ -63,11 +67,11 @@ function differencesUI(differences: DirtyChangeset, next: StateSnapshot) {
}

export default async function() {
ui.compTag();
process.env['HYDROLEAF_MODE'] = HydroleafMode.RenderToString;

// Get the current git hash for use in the output
git.long(async (_gitRev) => {
ui.compTag();

let compfile: Compfile;
try {
Expand All @@ -79,13 +83,28 @@ export default async function() {
}

// compfile.assets.gitRev = gitRev;
const contentCache = createContentCache();
let pages, templates;
try {
templates = await renderTemplates(
resolveAllTemplates(compfile),
compfile.assets
);
pages = await renderComponents(resolveAllPages(compfile));

let contentApiPages: PageComponentMap = {};

if (compfile.contentApi) {
const options = compfile.contentApi;
const paths: Array<string> = await contentCache.getAllPaths(
compfile.contentApi
);
paths.forEach((path) => {
contentApiPages[path] = options.template;
});
}

const allPages = { ...resolveAllPages(compfile), ...contentApiPages };
pages = await renderComponents(allPages);
} catch (err) {
console.log(err);
process.exit(1);
Expand Down
65 changes: 0 additions & 65 deletions src/generator/contentAPI.ts

This file was deleted.

12 changes: 4 additions & 8 deletions src/generator/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,12 @@ function createTwoStepMessage(msgGen: (...args: Array<any>) => string) {
};
}

export const loadingFalmerContent = createTwoStepMessage((count: number) =>
chalk`{white loading ${count.toString()} documents from falmer contentAPI... }`
export const savingState = createTwoStepMessage(
() => chalk`{white saving state file...}`
);

export const savingState = createTwoStepMessage(() =>
chalk`{white saving state file...}`
);

export const renderingComponents = createTwoStepMessage(() =>
chalk`{white rendering components to markup... }`
export const renderingComponents = createTwoStepMessage(
() => chalk`{white rendering components to markup... }`
);

export function compTag() {
Expand Down
6 changes: 0 additions & 6 deletions src/react-tree-walker.d.ts

This file was deleted.

83 changes: 49 additions & 34 deletions src/renderer.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom/server';
import {
getContentForElement,
getContentForElements,
} from './generator/contentAPI';
import * as ui from './generator/ui';
import * as PropTypes from 'prop-types';
import { ApolloProvider } from 'react-apollo';
import {ApolloProvider, getDataFromTree} from 'react-apollo';
import { ApolloClient, InMemoryCache, HttpLink } from 'apollo-client-preset';
import { StaticRouter } from 'react-router';
import {
Expand All @@ -17,12 +13,24 @@ import {
TemplateResultMap,
} from './types';

const ENDPOINT = 'https://falmer.sussexstudent.com/graphql';

function createFreshApolloClient() {
return new ApolloClient({
cache: new InMemoryCache(),
link: new HttpLink({
uri: ENDPOINT,
}),
ssrMode: true
});
}

export function createRenderBase(contentAPIStore: object, location: string | undefined = undefined) {
const client = new ApolloClient({
cache: new InMemoryCache(),
link: new HttpLink({
uri: 'https://falmer.sussexstudent.com',
}),
cache: new InMemoryCache(),
link: new HttpLink({
uri: ENDPOINT,
}),
ssrMode: true
});

Expand Down Expand Up @@ -69,24 +77,42 @@ export const renderHtml = (
Html: any,
children: any,
assets: object,
other: { inject?: object } = {}
other: { inject?: object, compOptions?: string } = {}
) => {
if (other.inject) {
((global as any)).mslInject = {
...(((global as any)).mslInject || {}),
...other.inject,
};
}
const finalElement: any = React.createElement(Html, { assets: assets }, children);

let additionalHead = [];

if (other.compOptions) {
additionalHead.push(other.compOptions);
}

const finalElement: any = React.createElement(Html, { assets: assets, additionalHead }, children);
return ReactDOM.renderToStaticMarkup(finalElement)
.replace('{head_content}', '');
};

export async function renderComponent(Component: any, props = {}, location: string | undefined = undefined) {
const remoteStore = await getContentForElement(
React.createElement(Component, props)
process.env['HYDROLEAF_MODE'] = HydroleafMode.RenderToComponent;

const finalElement: any = (
<ApolloProvider client={createFreshApolloClient()}>
<StaticRouter location={location} context={{}}>
<Component {...props} />
</StaticRouter>
</ApolloProvider>
);
return render(Component, props, remoteStore, HydroleafMode.RenderToString, location);

return getDataFromTree(finalElement).then(() => {
process.env['HYDROLEAF_MODE'] = HydroleafMode.RenderToString;

return ReactDOM.renderToStaticMarkup(finalElement as any);
});
}

export function renderTemplates(
Expand Down Expand Up @@ -123,37 +149,26 @@ export function renderTemplates(
return renderedTemplates;
}

function filterStoreForRequests(store: any, requests: Array<string>) {
const filteredStore: any = {};

requests.forEach((request) => (filteredStore[request] = store[request]));

return filteredStore;
}

export async function renderComponents(
pages: PageComponentMap
): Promise<PageResultMap> {
const renderedPages: PageResultMap = {};

const componentNames = Object.keys(pages);
const asElements = componentNames.map((pageName) => {
return React.createElement(pages[pageName]);
});

const [requests, store] = await getContentForElements(asElements);
const done = ui.renderingComponents();
componentNames.forEach((pageName, index) => {

await Promise.all(componentNames.map(async (pageName, _index) => {
const content = await renderComponent(
pages[pageName],
{ path: pageName },
pageName,
);
renderedPages[pageName] = {
name: pageName,
content: render(
pages[pageName],
{},
filterStoreForRequests(store, requests[index]),
HydroleafMode.RenderToString
),
content,
};
});
}));

done();

Expand Down
Loading

0 comments on commit 9c0df37

Please sign in to comment.