Initial commit.

This commit is contained in:
Dessalines 2020-08-22 23:54:33 -04:00
commit bf8a79e467
13 changed files with 5884 additions and 0 deletions

27
.gitignore vendored Normal file
View file

@ -0,0 +1,27 @@
dist
.fusebox
_site
.alm
.history
.git
build
.build
.idea
.jshintrc
.nyc_output
.sass-cache
.vscode
coverage
jsconfig.json
Gemfile.lock
node_modules
.DS_Store
*.map
*.log
*.swp
*~
test/data/result.json
package-lock.json
*.orig

4
README.md Normal file
View file

@ -0,0 +1,4 @@
# lemmy-isomorphic-ui
A trial run at making an isomorphic UI for lemmy, based off of MrFoxPro's [inferno-isomorphic-template](https://github.com/MrFoxPro/inferno-isomorphic-template).

76
fuse.ts Normal file
View file

@ -0,0 +1,76 @@
import { CSSPlugin, FuseBox, FuseBoxOptions, Sparky } from "fuse-box";
import path = require("path");
import TsTransformClasscat from "ts-transform-classcat";
import TsTransformInferno from "ts-transform-inferno";
/**
* Some of FuseBoxOptions overrides by ts config (module, target, etc)
* https://fuse-box.org/page/working-with-targets
*/
let fuse: FuseBox;
const fuseOptions: FuseBoxOptions = {
homeDir: "./src",
output: "dist/$name.js",
sourceMaps: { inline: false, vendor: false },
/**
* Custom TypeScript Transformers (compile Inferno tsx to ts)
*/
transformers: {
before: [TsTransformClasscat(), TsTransformInferno()]
}
};
const fuseClientOptions: FuseBoxOptions = {
...fuseOptions,
plugins: [
/**
* https://fuse-box.org/page/css-resource-plugin
* Compile Sass {SassPlugin()}
* Make .css files modules-like (allow import them like modules) {CSSModules}
* Make .css files modules like and allow import it from node_modules too {CSSResourcePlugin}
* Use them all and bundle with {CSSPlugin}
* */
CSSPlugin()
]
};
const fuseServerOptions: FuseBoxOptions = {
...fuseOptions
};
Sparky.task("clean", () => {
/**Clean distribute (dist) folder */
Sparky.src("dist")
.clean("dist")
.exec();
});
Sparky.task("config", () => {
fuse = FuseBox.init(fuseOptions);
fuse.dev();
});
Sparky.task("test", ["&clean", "&config"], () => {
fuse.bundle("client/bundle").test("[**/**.test.tsx]", null);
});
Sparky.task("client", () => {
fuse.opts = fuseClientOptions;
fuse
.bundle("client/bundle")
.target("browser@esnext")
.watch("client/**")
.hmr()
.instructions("> client/index.tsx");
});
Sparky.task("server", () => {
/**Workaround. Should be fixed */
fuse.opts = fuseServerOptions;
fuse
.bundle("server/bundle")
.watch("**")
.target("server@esnext")
.instructions("> [server/index.tsx]")
.completed(proc => {
proc.require({
// tslint:disable-next-line:no-shadowed-variable
close: ({ FuseBox }) => FuseBox.import(FuseBox.mainFile).shutdown()
});
});
});
Sparky.task("dev", ["&clean", "&config", "&client", "&server"], () => {
fuse.run();
});

39
package.json Normal file
View file

@ -0,0 +1,39 @@
{
"name": "lemmy-isomorphic-ui",
"scripts": {
"dev": "set NODE_ENV=development && node -r ts-node/register --inspect fuse.ts dev",
"test": "node -r ts-node/register --inspect fuse.ts test"
},
"dependencies": {
"@types/serialize-javascript": "^4.0.0",
"cookie-parser": "^1.4.3",
"express": "~4.17.1",
"inferno": "^7.4.3",
"inferno-create-element": "^7.4.3",
"inferno-hydrate": "^7.4.3",
"inferno-router": "^7.4.3",
"inferno-server": "^7.4.3",
"serialize-javascript": "^4.0.0"
},
"devDependencies": {
"@types/cookie-parser": "^1.4.1",
"@types/enzyme": "^3.1.10",
"@types/express": "^4.11.1",
"@types/jest": "^26.0.10",
"@types/node": "^14.6.0",
"enzyme": "^3.3.0",
"enzyme-adapter-inferno": "^1.3.0",
"fuse-box": "3.7.1",
"fuse-test-runner": "^1.0.16",
"inferno-devtools": "^7.4.3",
"inferno-test-utils": "^7.4.3",
"jest": "^26.4.2",
"jsdom": "16.4.0",
"jsdom-global": "3.0.2",
"ts-node": "^9.0.0",
"ts-transform-classcat": "^1.0.0",
"ts-transform-inferno": "^4.0.3",
"tslint-react-recommended": "^1.0.15",
"typescript": "^4.0.2"
}
}

View file

@ -0,0 +1,10 @@
.text {
color: brown;
font-size: 25pt;
}
.count {
color: blue;
}
.button {
color: red;
}

View file

@ -0,0 +1,19 @@
import "jsdom-global/register";
import { configure, mount, render, shallow } from "enzyme";
import InfernoEnzymeAdapter = require("enzyme-adapter-inferno");
import { should } from "fuse-test-runner";
import { Component } from "inferno";
import { renderToSnapshot } from "inferno-test-utils";
import About from "./About";
configure({ adapter: new InfernoEnzymeAdapter() });
export class AboutTest {
public "Should be okay"() {
const wrapper = mount(<About />);
wrapper.find(".button").simulate("click");
const countText = wrapper.find(".count").text();
should(countText)
.beString()
.equal("1");
}
}

View file

@ -0,0 +1,32 @@
import { Component } from "inferno";
import "./About.css";
interface IState {
clickCount: number;
}
interface IProps {}
export default class About extends Component<IProps, IState> {
constructor(props) {
super(props);
this.state = {
clickCount: 0
};
this.increment = this.increment.bind(this);
}
protected increment() {
this.setState({
clickCount: this.state.clickCount + 1
});
}
public render() {
return (
<div>
Simple Inferno SSR template
<p className="text">Hello, world!</p>
<button onClick={this.increment} className="button">
Increment
</button>
<p className="count">{this.state.clickCount}</p>
</div>
);
}
}

View file

@ -0,0 +1,36 @@
import { Component, render } from "inferno";
import { Link, Route, StaticRouter, Switch } from "inferno-router";
import About from "../About/About";
import Home from "../Home/Home";
interface IState {}
interface IProps {name: string}
export default class App extends Component<IProps, IState> {
constructor(props) {
super(props);
}
public render() {
return (
<div>
<div>
<h3>{this.props.name}</h3>
<div>
<Link to="/Home">
<p>Home</p>
</Link>
</div>
<div>
<Link to="/About" className="link">
<p>About</p>
</Link>
</div>
</div>
<div>
<Switch>
<Route exact path="/Home" component={Home} />
<Route exact path="/About" component={About} />
</Switch>
</div>
</div>
);
}
}

View file

@ -0,0 +1,22 @@
import { Component } from "inferno";
interface IState {}
interface IProps {}
export default class Home extends Component<IProps, IState> {
constructor(props) {
super(props);
}
protected click() {
/**
* Try to debug next line
*/
console.log("hi");
}
public render() {
return (
<div>
Home page
<button onClick={this.click}>Click me</button>
</div>
);
}
}

21
src/client/index.tsx Normal file
View file

@ -0,0 +1,21 @@
import { Component } from "inferno";
import { hydrate } from 'inferno-hydrate';
import { BrowserRouter } from "inferno-router";
import App from "./components/App/App";
import { initDevTools } from "inferno-devtools";
declare global {
interface Window {
isoData: {
name: string,
}
}
}
const wrapper = (
<BrowserRouter>
<App name={window.isoData.name}/>
</BrowserRouter>
);
initDevTools();
hydrate(wrapper, document.getElementById("root"));

56
src/server/index.tsx Normal file
View file

@ -0,0 +1,56 @@
import cookieParser = require("cookie-parser");
import * as serialize from "serialize-javascript";
import * as express from "express";
import { StaticRouter } from "inferno-router";
import { renderToString } from "inferno-server";
import path = require("path");
import App from "../client/components/App/App";
const server = express();
const port = 1234;
server.use(express.json());
server.use(express.urlencoded({ extended: false }));
server.use("/static", express.static(path.resolve("./dist/client")));
server.use(cookieParser());
server.get("/*", (req, res) => {
const context = {} as any;
const isoData = {
name: "fishing sux",
}
const wrapper = (
<StaticRouter location={req.url} context={context}>
<App name={isoData.name} />
</StaticRouter>
);
if (context.url) {
return res.redirect(context.url);
}
res.send(`
<!doctype html>
<html>
<head>
<title>My Universal App</title>
<script>window.isoData = ${serialize(isoData)}</script>
</head>
<body>
<div id='root'>${renderToString(wrapper)}</div>
<script src='./static/bundle.js'></script>
</body>
</html>
`);
});
let Server = server.listen(port, () => {
console.log(`http://localhost:${port}`);
});
/**
* Used to restart server by fuseBox
*/
export async function shutdown() {
Server.close();
Server = undefined;
}

12
tsconfig.json Normal file
View file

@ -0,0 +1,12 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "esnext",
"sourceMap": true,
"jsx": "preserve",
"importHelpers": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true
},
"exclude": ["node_modules", "fuse.ts"]
}

5530
yarn.lock Normal file

File diff suppressed because it is too large Load diff