mirror of
https://github.com/LemmyNet/lemmy-ui.git
synced 2024-11-25 07:36:37 +00:00
Initial commit.
This commit is contained in:
commit
bf8a79e467
27
.gitignore
vendored
Normal file
27
.gitignore
vendored
Normal 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
4
README.md
Normal 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
76
fuse.ts
Normal 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
39
package.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
10
src/client/components/About/About.css
Normal file
10
src/client/components/About/About.css
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
.text {
|
||||||
|
color: brown;
|
||||||
|
font-size: 25pt;
|
||||||
|
}
|
||||||
|
.count {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
.button {
|
||||||
|
color: red;
|
||||||
|
}
|
19
src/client/components/About/About.test.tsx
Normal file
19
src/client/components/About/About.test.tsx
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
32
src/client/components/About/About.tsx
Normal file
32
src/client/components/About/About.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
36
src/client/components/App/App.tsx
Normal file
36
src/client/components/App/App.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
22
src/client/components/Home/Home.tsx
Normal file
22
src/client/components/Home/Home.tsx
Normal 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
21
src/client/index.tsx
Normal 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
56
src/server/index.tsx
Normal 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
12
tsconfig.json
Normal 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"]
|
||||||
|
}
|
Loading…
Reference in a new issue