added project files

This commit is contained in:
Andreas Schaafsma 2022-01-14 18:21:37 +00:00
parent b3d759b8f7
commit 33e18d8f2a
21 changed files with 5080 additions and 0 deletions

9
app/build.hxml Normal file
View File

@ -0,0 +1,9 @@
-lib haxe-loader
-lib react
-lib react-router-4
-cp src
-js app.js
-main App
-D react_hot

14
app/hmm.json Normal file
View File

@ -0,0 +1,14 @@
{
"dependencies": [
{
"name": "haxe-loader",
"type": "haxelib",
"version": "0.9.0"
},
{
"name": "react",
"type": "haxelib",
"version": "1.4.0"
}
]
}

25
app/package.json Normal file
View File

@ -0,0 +1,25 @@
{
"name": "webpack-haxe-example",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"css-loader": "^2.0.2",
"file-loader": "^3.0.1",
"haxe-loader": "^0.9.0",
"html-webpack-plugin": "^3.2.0",
"prop-types": "^15.6.2",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"style-loader": "^0.23.1",
"webpack": "^4.28.2",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.12"
},
"scripts": {
"start": "webpack-dev-server",
"build:dev": "webpack",
"build": "webpack"
}
}

131
app/readme.md Normal file
View File

@ -0,0 +1,131 @@
# Modular Haxe JS with Webpack
This project demonstrates the creation of a Haxe-JavaScript modular project leveraging Webpack
for bundling (code and assets) and lazy loading.
It's really easy and absolutely transparent in the code!
**Note: this is a "vanilla DOM" example - no 3rd party library involved!**
## How it works
### Leveraging Webpack features
You will need to get familiar with how Webpack works - thankfully the documentation
is excellent nowadays: [https://webpack.js.org/](https://webpack.js.org/)
A project aims at creating a Webpack loader for Haxe:
[https://github.com/jasononeil/webpack-haxe-loader](https://github.com/jasononeil/webpack-haxe-loader)
Every asset dependency should be explictely required, so Webpack knows what to include
in the output folder. With the right configuration, small assets can even be inlined in
the bundle to reduce the number of requests.
```haxe
Webpack.require('./index.css');
var img = new Image();
img.src = Webpack.require('./logo.png');
```
Even the HTML page is generated by a plugin, so everything has to go through Webpack.
### Haxe JS code splitting
Haxe JS isn't normally capable of code splitting, but the core functionality was
developped within the (Webpack-free) Haxe Modular project:
[https://github.com/elsassph/haxe-modular](https://github.com/elsassph/haxe-modular)
The Haxe Webpack loader will leverage the code splitting feature when you request
modules asynchronously:
```haxe
import com.Foo;
...
// Extract Foo (and dependencies) into a separate bundle
Webpack.load(Foo).then(function(_) {
// Foo is now loaded
var foo = new Foo();
});
```
### Webpack config
Webpack's "magic" is configured in the `webpack.config.js`. It is a very powerful and
flexible system which is documented here: [https://webpack.js.org/](https://webpack.js.org/)
The basics is that the `haxe-loader` allow to "require" an
[HXML file](https://haxe.org/manual/compiler-usage-hxml.html),
which in turn will provide the (splitted) JS output to Webpack.
This feature is added as a "rule" in the config:
```
{
test: /\.hxml$/,
loader: 'haxe-loader',
options: {
extra: `-D some_extra=arguments`,
debug: debugMode
}
},
```
HXMLs can be require directly from JS code, or as an entry point, which allows to
make a pure Haxe Webpack project:
```
entry: {
app: './build.hxml'
},
```
## Development
### Tools
Using [yarn](https://yarnpkg.com) for node modules is recommended:
npm install yarn -g
Using [hmm](https://github.com/andywhite37/hmm) for haxelibs is recommended:
haxelib --global install hmm
haxelib --global run hmm setup
### Installation
Install npm and haxe dependencies:
yarn install
hmm install
### Running
Then start Webpack webserver, open `http://localhost:9000`, and enjoy live reload:
yarn start
### Hot Module Replacement
Hot-Module Replacement is the technique allowing, while your browser is showing your
live application, to hot-reload CSS and the Haxe modules!
React views in asynchronous modules will automatically refresh themselves if you edit
and save: Webpack will recompile the project, reload the module, and re-render the
views without losing state. un and try editing `Foo.hx`!
For that you only need to:
- add `-D react_hot` in your `hxml`,
- call `ReactHMR.autoRefresh` after the main render (see `App.hx`),
- run in live debug mode (`yarn start`).
### Releasing
To build the project statically, run:
yarn build
For a production release:
export NODE_ENV=production
yarn build -p

15
app/src/App.css Normal file
View File

@ -0,0 +1,15 @@
body {
font-family: Arial;
margin: 0;
}
h1 {
/* margin: 10px; */
}
main{
margin: 20px;
background-color: #eee;
min-height: 300px;
padding: 10px;
}

43
app/src/App.hx Normal file
View File

@ -0,0 +1,43 @@
import react.ReactMacro.jsx;
import react.router.ReactRouter;
import react.router.BrowserRouter;
import react.router.Route;
import react.router.Switch;
import react.router.bundle.Bundle;
import Webpack.*;
import Root;
class App {
static var STYLES = require('./App.css');
static var Rewt = ReactRouter.withRouter(Root);
static public function main() {
new App();
}
public function new() {
var root = createRoot();
var rootComponent = react.ReactDOM.render(jsx('
<$BrowserRouter>
<$Switch>
<$Rewt/>
</$Switch>
</$BrowserRouter>
'), root);
#if debug
ReactHMR.autoRefresh(rootComponent);
#end
}
function createRoot() {
var current = js.Browser.document.getElementById('root');
if (current != null) return current;
current = Dom.div();
current.id = 'root';
Dom.body().appendChild(current);
return current;
}
}

16
app/src/Dom.hx Normal file
View File

@ -0,0 +1,16 @@
class Dom {
static var TEMP = js.Browser.document.createDivElement();
inline static public function div() {
return js.Browser.document.createDivElement();
}
inline static public function html(html: String) {
TEMP.innerHTML = html;
return TEMP.firstElementChild;
}
inline static public function body() {
return js.Browser.document.body;
}
}

83
app/src/Root.hx Normal file
View File

@ -0,0 +1,83 @@
import com.Foo;
import com.Foo2;
import components.Header;
import components.HomeContent;
import react.ReactMacro.jsx;
import react.ReactComponent;
import react.React.CreateElementType;
import react.router.ReactRouter;
import react.router.Route.RouteRenderProps;
import react.router.Route;
private typedef RootState = {
route: String,
?component: react.React.CreateElementType
}
private typedef RootProps = {
> RouteRenderProps,
}
class Root extends react.ReactComponentOf<RootProps,RootState> {
public function new() {
super();
state = { route:'' };
}
override function componentDidMount() {
trace("wattfak");
switch (state.route) {
default:
Webpack.load(Foo).then(function(_) {
setState(cast { component:Foo });
trace("foo");
});
}
}
function yeet(){
//state.route="yeet";
//trace(state);
//setState({route:"yeetnt"});
}
override function render() {
return jsx('
<div>
<Header/>
<!--Current path is ${props.location.pathname} and component is ${state.route}-->
<!--${renderContent()}-->
<main>
<$Route exact="true" path="/">
<HomeContent/>
</$Route>
<$Route path="/projects">
<h1>Projects</h1>
</$Route>
<$Route path="/links">
<h1>Links</h1>
</$Route>
<$Route path="/gameservers">
<h1>Game Servers</h1>
<p></p>
</$Route>
</main>
</div>
');
}
function renderContent() {
if(state.route != props.location.pathname){
state.route = props.location.pathname;
}
if (state.component == null)
return jsx('
<span>Loading...</span>
');
else
return jsx('
<state.component />
');
}
}

View File

@ -0,0 +1,12 @@
import react.ReactComponent;
import react.router.Route.RouteRenderProps;
@:expose('default')
class MyBundle extends ReactComponentOfProps<RouteRenderProps> {
// If you want to execute code when this bundle is _first_ loaded:
public static function onLoad() {
// ...
}
// ...
}

14
app/src/com/Foo.css Normal file
View File

@ -0,0 +1,14 @@
.foo {
margin: 10px;
padding: 10px;
background: #eee;
}
.foo .yeah {
margin-top: 20px;
}
.foo .yeah p {
margin: 0;
border-bottom: solid 1px red;
}

27
app/src/com/Foo.hx Normal file
View File

@ -0,0 +1,27 @@
package com;
import react.ReactComponent;
import react.ReactMacro.jsx;
import Webpack.*;
class Foo extends ReactComponent {
static var STYLES = require('./Foo.css');
static var IMG = require('./bug.png');
static var CONFIG = require('../config.json');
public function yeet(){
trace(state);
}
override function render() {
return jsx('
<div className="foo">
<img src=$IMG/> ${CONFIG.hello}!
<p onClick=${yeet}> ${CONFIG.yeet}!</p>
<hr/>
Let\'s do some HRM guys<br/>
</div>
');
}
}

24
app/src/com/Foo2.hx Normal file
View File

@ -0,0 +1,24 @@
package com;
import react.ReactComponent;
import react.ReactMacro.jsx;
import Webpack.*;
class Foo2 extends ReactComponent {
static var STYLES = require('./Foo.css');
static var IMG = require('./bug.png');
static var CONFIG = require('../config.json');
override function render() {
return jsx('
<div className="foo2">
nooo
<img src=$IMG/> ${CONFIG.hello}!
<hr/>
Let\'s do some HRM guys<br/>
</div>
');
}
}

BIN
app/src/com/bug.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 B

View File

@ -0,0 +1,14 @@
header {
margin: 10px;
padding: 10px;
background: #eee;
}
header nav a{
margin-right: 10px;
}
.selected{
color:red;
}
.logo{
font-size: 3vw;
}

View File

@ -0,0 +1,36 @@
package components;
import react.ReactMacro.jsx;
import react.ReactComponent;
import Webpack.*;
import react.router.NavLink;
class Header extends ReactComponent{
static var STYLES = require('./Header.css');
public function yeet(){
props.foo();
}
override function render() {
return jsx('
<header>
<div className="logo">
subsonics
</div>
<nav>
<$NavLink exact="true" to="/" activeClassName="selected">
Home
</NavLink>
<$NavLink to="/links" activeClassName="selected">
Links
</NavLink>
<$NavLink to="/projects" activeClassName="selected">
Projects
</NavLink>
<$NavLink to="/gameservers" activeClassName="selected">
Game Servers
</NavLink>
</nav>
</header>
');
}
}

View File

View File

@ -0,0 +1,22 @@
package components;
import react.ReactMacro.jsx;
import react.ReactComponent;
import js.Browser;
import Webpack.*;
class HomeContent extends ReactComponent {
static var STYLES = require('./HomeContent.css');
public function yeet(){
trace(state);
}
override public function render() {
return jsx('
<p onClick=${yeet}>
kak
</p>
');
}
}

View File

@ -0,0 +1,34 @@
package components;
import react.ReactMacro.jsx;
import react.ReactComponent;
import react.router.BrowserRouter;
import react.router.Route;
import react.router.Switch;
import react.router.bundle.Bundle;
class MainRouter extends ReactComponent {
override public function render() {
return jsx('
<$BrowserRouter>
<$Switch>
<!-- Using default loader component (`<div className="loader" />`) -->
<!-- and default error component (`<div className="error" />`) -->
<!-- /!\\ Warning: your component should have the `@:expose("default")` meta -->
<!-- See example below in "Bundle initialization code" -->
<$Route
path="/bundle1"
component=${Bundle.load(first.FirstBundle)}
/>
<!-- Using custom loader and/or error component -->
<!-- The error component will get an `error` prop with the load error as `Dynamic` -->
<$Route
path="/bundle2"
component=${Bundle.load(second.SecondBundle, CustomLoader, CustomError)}
/>
</$Switch>
</$BrowserRouter>
');
}
}

4
app/src/config.json Normal file
View File

@ -0,0 +1,4 @@
{
"hello": "This is an asynchronous module" ,
"yeet": "yote"
}

122
app/webpack.config.js Normal file
View File

@ -0,0 +1,122 @@
//
// Webpack documentation is fairly extensive,
// just search on https://webpack.js.org/
//
// Be careful: there are a lot of outdated examples/samples,
// so always check the official documentation!
//
// Plugins
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// Options
const buildMode = process.env.NODE_ENV || 'development';
const debugMode = buildMode !== 'production';
const dist = __dirname + '/www/';
// Sourcemaps: https://webpack.js.org/configuration/devtool/
// - 'cheap-module-source-map': fastest in Haxe-only setup
// - 'eval-source-map': fast, but JS bundle is somewhat obfuscated
// - 'source-map': slow, but JS bundle is readable
// - undefined: no map, and JS bundle is readable
const sourcemapsMode = debugMode ? 'cheap-module-source-map' : undefined;
//
// Configuration:
// This configuration is still relatively minimalistic;
// each section has many more options
//
module.exports = {
mode: 'development',
// List all the JS modules to create
// They will all be linked in the HTML page
entry: {
app: './build.hxml'
},
// Generation options (destination, naming pattern,...)
output: {
path: dist,
publicPath: '/',
filename: '[name].[hash:7].js'
},
// Module resolution options (alias, default paths,...)
resolve: {
extensions: ['.js', '.json']
},
// Sourcemaps option for development
devtool: sourcemapsMode,
// Live development server (serves from memory)
devServer: {
contentBase: dist,
compress: true,
host: "0.0.0.0",
inline: true,
historyApiFallback: true,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
"Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"},
public: "https://dev.subsonics.nl",
port: 9000,
overlay: true,
hot: true,
disableHostCheck: true
},
// List all the processors
module: {
rules: [
// Haxe loader (through HXML files for now)
{
test: /\.hxml$/,
loader: 'haxe-loader',
options: {
// Additional compiler options added to all builds
extra: '-D build_mode=' + buildMode,
debug: debugMode,
logCommand: true
}
},
// Static assets loader
// - you will need to adjust for webfonts
// - you may use 'url-loader' instead which can replace
// small assets with data-urls
{
test: /\.(gif|png|jpg|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[hash:7].[ext]'
}
},
// CSS processor/loader
// - this is where you can add sass/less processing,
// - also consider adding postcss-loader for autoprefixing
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}
]
},
// Plugins can hook to the compiler lifecycle and handle extra tasks
plugins: [
// HMR: enable globally
new webpack.HotModuleReplacementPlugin(),
// HMR: prints more readable module names in the browser console on updates
new webpack.NamedModulesPlugin(),
// HMR: do not emit compiled assets that include errors
new webpack.NoEmitOnErrorsPlugin(),
// Like generating the HTML page with links the generated JS files
new HtmlWebpackPlugin({
title: 'Webpack + Haxe example'
})
// You may want to also:
// - finer control of minify/uglify process using UglifyJSPlugin,
// - extract the small CSS chunks into a single file using ExtractTextPlugin
// - avoid modules duplication using CommonsChunkPlugin
// - inspect your JS output weight using BundleAnalyzerPlugin
],
};

4435
app/yarn.lock Normal file

File diff suppressed because it is too large Load Diff