Skip to content
Permalink
Browse files

Speed, context and helpers

  • Loading branch information...
Koen Bok Koen Bok
Koen Bok authored and Koen Bok committed Nov 18, 2018
1 parent 311ab4e commit a3867d1e69fbce7aea930dde98577d74a75613fb
Showing with 218 additions and 92 deletions.
  1. +0 −6 Makefile
  2. +3 −2 project/components/Template.tsx
  3. +12 −1 project/pages/about.tsx
  4. +87 −0 project/pages/test.tsx
  5. +12 −0 readme.md
  6. +15 −21 src/cli.ts
  7. +11 −0 src/{project.ts → commands.ts}
  8. +11 −4 src/compiler.ts
  9. +2 −14 src/context.ts
  10. +0 −16 src/env.ts
  11. +16 −10 src/{react.tsx → helpers.tsx}
  12. +7 −2 src/index.ts
  13. +23 −14 src/render.ts
  14. +0 −2 src/server.ts
  15. +6 −0 src/types.ts
  16. +13 −0 src/utils.ts
@@ -13,12 +13,6 @@ serve: bootstrap
build: bootstrap
./node_modules/.bin/ts-node -P $(project) src/cli.ts build --project=$(project)

# test: bootstrap
# ./node_modules/.bin/jest --watch

# docs: bootstrap
# ./node_modules/.bin/typedoc --out dist/docs

publish: git-check dist
yarn publish
make project
@@ -1,12 +1,13 @@
import * as React from "react";
import { Development, StyledSheet } from "monobase";
import { relative, Development, StyledSheet, useContext } from "monobase";

export default function Template(props) {
const context = useContext();
return (
<html>
<head>
<StyledSheet app={props.children} />
<link rel="stylesheet" href="/static/styles.css" />
<link rel="stylesheet" href={relative("/static/styles.css")} />
</head>
<body>
{props.children}
@@ -1,6 +1,17 @@
import * as React from "react";
import Template from "components/Template";
import { useContext } from "monobase";

const style = { font: "11px/1.6em Menlo" };

export default function render() {
return <Template>About this site.</Template>;
const context = useContext();
return (
<Template>
<div style={style}>
<h2>Context</h2>
<pre style={style}>{JSON.stringify(context, null, 4)}</pre>
</div>
</Template>
);
}
@@ -0,0 +1,87 @@
import * as React from "react";

import Template from "components/Template";

import Grid from "components/examples/Grid";
import Timer from "components/examples/Timer";
import Mouse from "components/examples/Mouse";
import Button from "components/examples/Button";
import Styled from "components/examples/Styled";
import Colors from "components/examples/Colors";
import Cookie from "components/examples/Cookie";
import Visible from "components/examples/Visible";
import Unsplash from "components/examples/Unsplash";

export default function render() {
return (
<Template>
<span style={{ textAlign: "center" }}>
<section style={{ padding: "60px" }}>
<h1>Welcome to Monobase</h1>
<p>A simple React based static site generator</p>
</section>
<Example title="CSS Styled Component">
<Styled />
</Example>
<Example title="Button">
<Button />
</Example>
<Example title="Mouse Location">
<Mouse />
</Example>
<Example title="Time">
<Timer />
</Example>
<Example title="Visible">
<Visible inset={100}>
I'm like a refrigerator light. Or Schrödinger's cat.
</Visible>
</Example>
<Example title="Random Image Grid">
<Grid
width={600}
height={300}
columns={4}
rows={4}
gap={10}
cell={props => (
<Visible>
<Unsplash {...props} />
</Visible>
)}
/>
</Example>
<Example title="Enter a color name">
<Colors />
</Example>
<Example title="Persistent Cookie">
<Cookie />
</Example>
</span>
</Template>
);
}

function Example(props) {
return (
<section
style={{
textAlign: "center",
padding: "80px 10px"
}}
>
<h3
style={{
fontSize: 30,
fontWeight: 700,
margin: 0,
paddingBottom: 40,
lineHeight: 1
}}
>
{props.title}
</h3>
<div style={{ textAlign: "center" }}>{props.children}</div>
</section>
);
}
@@ -100,13 +100,25 @@ Monobase generates a combined bundle for all of your pages, which contains all t

#### Todo

v1

- ~~Make project based (non relative) path loading work.~~
- ~~Improve dynamic component discovery (automagically, tips welcome).~~
- ~~Harden component hydration with unique names based on component file hashes.~~
- ~~Minified `component.js` settings for production.~~
- ~~Potential speedup: separate ts type checking from compilation, like `awesome-ts-loader`.~~
- Make a development page listing all components at `/components` where you can click them to see an isolated version.

v2

- Set of great helpers (context, project, relative paths, url to path)
- Optimize performance
- Reduce parsing
- Make React external?
- Redesign hydration
- Best debugging experience
- Testing / CI setup out of the box

#### Deployment

The output of `make build` is just a web project that you can deploy anywhere, like Amazon S3 sites or Netlify. I myselfs also really like [Zeit](https://zeit.co/) or [Netlify](https://netlify.com) for static websites.
@@ -13,16 +13,12 @@ import * as minimist from "minimist";
import * as openport from "first-open-port";
import * as address from "my-local-ip";
import * as reachable from "is-reachable";

import chalk from "chalk";

import * as browser from "./browser";
import * as project from "./project";
import * as types from "./types";
import { env } from "./env";
import * as commands from "./commands";

process.on("unhandledRejection", (reason, p) => {
console.log("Unhandled Rejection at: Promise", p, "reason:", reason);
console.error("Unhandled Rejection at: Promise", p, "reason:", reason);
});

const exit = () => {
@@ -44,22 +40,21 @@ const main = async () => {
build = argv.build;
}

env.project = {
...env.project,
const project = {
path: path.resolve(argv.project || process.cwd()),
build: build
build: build,
config: {
pages: "pages",
static: "static",
components: "components",
componentScript: "/components.js"
}
};

if (!fs.existsSync(path.join(env.project.path, env.project.config.pages))) {
return console.log(
`The path "${
env.project.path
}" does not look like a project folder, the pages directory is missing.`
);
}
commands.check(project);

tsConfigPaths.register({
baseUrl: env.project.path,
baseUrl: project.path,
paths: {}
});

@@ -70,7 +65,7 @@ const main = async () => {
port = await openport(port, port + 100);

const open = argv.browser || true;
await project.serve(env.project, port);
await commands.serve(project, port);

const prettyHost = async (hosts: string[], port: number) => {
for (let host of hosts) {
@@ -92,9 +87,8 @@ const main = async () => {

console.log(chalk.bgWhite.black(" MONOBASE "), chalk.green(url));
} else if (command === "build") {
const buildPath =
argv.path || argv.p || path.join(env.project.path, "build");
project.build(env.project, buildPath);
const buildPath = argv.path || argv.p || path.join(project.path, "build");
commands.build(project, buildPath);
} else {
usage();
}
@@ -3,6 +3,7 @@ import chalk from "chalk";

import * as fs from "fs";
import * as _ from "lodash";
import * as path from "path";
import * as prettyBytes from "pretty-bytes";
import * as utils from "./utils";
import * as types from "./types";
@@ -31,4 +32,14 @@ export const build = async (project: types.Project, path: string) => {
console.log(chalk.gray(`${stats.files} files, ${prettyBytes(stats.bytes)}`));
};

export const check = (project: types.Project) => {
if (!fs.existsSync(path.join(project.path, project.config.pages))) {
return console.log(
`The path "${
project.path
}" does not look like a project folder, the pages directory is missing.`
);
}
};

export const serve = server.serve;
@@ -2,12 +2,11 @@ import * as fs from "fs";
import * as path from "path";
import * as MemoryFS from "memory-fs";
import * as webpack from "webpack";
import * as types from "./types";
import * as utils from "./utils";

export const ConfigDefaults = {
production: false,
cache: false
cache: false,
context: {}
};

type ConfigOptions = typeof ConfigDefaults;
@@ -47,7 +46,15 @@ export const Config = (
}
]
},
plugins: options.production ? productionPlugins : []
plugins: options.production
? productionPlugins
: [
new webpack.DefinePlugin({
"process.env": {
context: JSON.stringify(options.context)
}
})
]
};
};

@@ -2,18 +2,6 @@ import * as path from "path";
import * as utils from "./utils";
import * as types from "./types";

export const page = path => {
try {
return require(path).context;
} catch (error) {
return {};
}
};

export const project = (project: types.Project) => {
return utils
.glob(path.join(project.path, project.config.pages))
.map(pagePath => {
return {};
});
export const create = (project: types.Project, path: string): types.Context => {
return { project: project, path: path, url: utils.projectURLForPath(path) };
};

This file was deleted.

@@ -2,7 +2,7 @@ import * as path from "path";
import * as React from "react";
import { renderToString } from "react-dom/server";
import * as styled from "styled-components";
import { env } from "./env";
import * as types from "./types";

// The hack starts here
const { StyleSheet } = styled[
@@ -23,23 +23,29 @@ export const StyledSheet: React.SFC<{ app: React.ReactNode }> = props => {
};

export const Development = () => {
if (env.project.build === "debug") {
return <script src={env.project.config.componentScript} />;
const context = useContext();
if (context.project.build === "debug") {
return <script src={context.project.config.componentScript} />;
} else {
return null;
}
};

// Hook to use the current project
export const useProject = () => {
return env.project;
export const useContext = (): types.Context => {
if (!process.env["context"]) {
throw Error(
"process.env.context is missing. You might be using useContext in a Dynamic component? "
);
}
// This gets inserted by webpack on build
return (process.env["context"] as any) as types.Context;
};

// Todo
export const usePageContext = () => {
return env.context;
export const useProject = (): types.Project => {
return useContext().project;
};

export const relative = (to: string) => {
return path.relative(env.context.path || "/", to);
const context = useContext();
return path.relative(context.url, to);
};
@@ -1,3 +1,8 @@
export { Project } from "./types";
export { Dynamic } from "./component";
export { useProject, Development, StyledSheet } from "./react";
export {
relative,
useProject,
useContext,
Development,
StyledSheet
} from "./helpers";

0 comments on commit a3867d1

Please sign in to comment.
You can’t perform that action at this time.