In this post we are going to compare Mint with JavaScript, more specifically we are going to compare it with the equivalent JavaScript stack because Mint is also a framework.
This comparison will cover the following layers:
- Language
- Type checking
- Development Server
- View
- State
- Routing
- Networking
- Testing
- Error Messages
- Formatting
- Production Build
Each layer has the following sections:
- The JavaScript implementation with an example
- The Mint implementation with an example
- Comparison of the implementations
- Statistics (if applicable)
Beware: it covers a lot of features so this is a lengthy article.
Language
Comparing the two languages is tricky because they are very different, so I'll just try to give a small definition for both.
There are examples through out the post which gives you a general idea on the syntax an semantics.
JavaScript
Wikipedia defines it as:
JavaScript (/ˈdʒɑːvəˌskrɪpt/),[7] often abbreviated as JS, is a high-level, interpreted programming language. It is a language which is also characterized as dynamic, weakly typed, prototype-based and multi-paradigm.
Mint
Mint doesn't have an official definition yet, so I'll try to do my best to summarize it in a short paragraph:
Mint is a programming language which compiles to JavaScript. Created specifically for writing client side web applications. It is fundamentally strongly typed, functional with some object-oriented parts sprinkled in.
Type checking
The languages should support some sort of type checking which is important because it makes our code safer and less buggy.
JavaScript
For JavaScript there are third party tools for type checking, like Flow which we will use, but before we can do actual type checking we need to compile our typed JavaScript (with type annotations) to regular JavaScript (removing type annotations) which the browser can use.
For that we will use Babel so we need to install some packages to make it work: babel-core
babel-preset-react
(which includes the Flow preset for some reason) also we install babel-preset-es2015
and babel-preset-stage-0
to make advanced JavaScript features available.
To configure babel we need to create a .babelrc
file:
{
"presets": ["es2015", "react", "stage-0"]
}
Also we need to install flow-bin
to do the actual type checking and flow-typed
to install type definitions for the packages we use.
Mint
Mint comes with it's own type checker, so you don't need to do anything, it's automatically working under the hood.
Comparison
To get type checking in JavaScript you need a third party tool, in Mint it's built in.
Statistics
Statistics | JavaScript | Mint |
---|---|---|
Lines of code | 4 | 0 |
Third party packages |
6 babel-core babel-preset-react babel-preset-es2015 babel-preset-stage-0 flow-bin flow-typed
|
0 |
Development Server
Our development environment should be able to to the following things:
- compile our code into a single file
- recompile the code when the source files change, and refresh the browser
- serve static files from the directory
- provide error messages if there is a syntax or type error
- fall back to the
index.html
file if the route does not match a static file
JavaScript
To compile our code we can use Webpack with the webpack
webpack-cli
and webpack-dev-server
packages and for using Babel we need the babel-loader
package.
After installing them we configure them using the webpack.config.js
file:
const path = require("path");
module.exports = {
context: path.resolve(__dirname, "src"),
mode: 'development',
// The main.jsx will be compiled
entry: {
main: ["main.jsx"]
},
// This tells webpack how to resolve things
resolve: {
modules: [path.resolve("./src"), "node_modules"],
extensions: [".jsx"]
},
module: {
// This tells webpack to use babel
rules: [
{
test: /\.jsx$/,
use: {
loader: 'babel-loader',
}
}
]
},
// Configuration for the development server
devServer: {
// Serve static files from the public folder
contentBase: './public',
// Fallback to the index.html
historyApiFallback: {
rewrites: [
{
from: '/./',
to: '/'
}
]
}
}
}
In the configuration:
- we are transforming our code using the Babel with the
babel-loader
package - setting up the fall back to
index.html
for the server - specify which files to compile and in which directories
- specify the static file directory
- specify the main file
After that we need to create the actual public/index.html
file which will be served:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="/main.js"></script>
</body>
</html>
After that the development server can be started with: webpack-dev-server
Mint
In Mint the development server is built into the binary. After initializing our project with mint init
the only thing we need to do is to start it with: mint start
Comparison
In Mint it's a built in feature, while in JavaScript you need to use a third party package to achieve the same setup.
Statistics
Statistics | JavaScript | Mint |
---|---|---|
Lines of code | 44 | 0 |
Third party packages |
4 webpack webpack-dev-server webpack-cli babel-loader
|
0 |
View
For the view layer the following needs to be met:
- the styling should be scoped to the current component and it should be written in CSS, in the same file
- the properties of component should be type checked (preferably at compile time but runtime is OK), also default value for them should be provided
For the sake of the example, we are going to implement a simple counter component:
- it should display a counter
- it should have two buttons, one for decrementing the counter and one for incrementing it
- the background color should be reddish (
orangered
) if it's below zero, and greenish (limegreen
) if it's above ten
JavaScript
Flow automatically checks prop types we just need to create a type for them and use it, for the default properties we can use a static class property, and for styling we can use styled-components.
// @flow
import React, { Component } from "react";
import styled from "styled-components";
/* This is the styling for the base div. */
const BaseDiv = styled.div`
background: ${props => props.background};
border-radius: 5px;
transition: 320ms;
display: flex;
padding: 20px;
margin: 20px;
`
/* This is the styling for the counter span. */
const CounterSpan = styled.span`
font-family: sans;
font-size: 20px;
padding: 0 20px;
`
/* These are the property type definitons. */
type Props = {
onIncrement: () => void,
onDecrement: () => void,
counter: number
};
export default class Counter extends Component<Props> {
/* These are the default property values. */
static defaultProps = {
onIncrement: () => null,
onDecrement: () => null,
counter: 0
}
/* This is a function to return the background color. */
background () {
const { counter } = this.props
if (counter >= 10) {
return "lightgreen"
} else {
if (counter < 0) {
return "orangered"
} else {
return "#F2F2F2"
}
}
}
/* Renders the component. */
render () {
const { counter, onDecrement, onIncrement} = this.props
return <BaseDiv background={this.background()}>
<button onClick={() => onDecrement()}>
Decrement
</button>
<CounterSpan>
{ counter }
</CounterSpan>
<button onClick={() => onIncrement()}>
Increment
</button>
</BaseDiv>
}
}
Also to be able to display our counter we need to add it to the DOM.
// @flow
/* Default react imports. */
import React, { Component } from "react";
import styled from "styled-components";
import ReactDom from "react-dom";
/* Import the counter component. */
import Counter from './counter.jsx';
/* The base style. */
const StyledDiv = styled.div`
justify-content: center;
flex-direction: column;
align-items: center;
font-family: sans;
display: flex;
height: 100vh;
`
/* This is our main component. */
class Main extends Component {
render () {
return <StyledDiv><Counter/></StyledDiv>
}
}
/* Get the root element. */
const root = document.getElementById('root')
/* If there is a root element render the main component. */
if (root) {
ReactDOM.render(<Main/>, root)
}
Mint
In Mint you can define properties one-by-one with type and default value, styling is done with style
blocks.
component Counter {
/* Here we are defining the properties of the counter. */
property onIncrement : Function(a) = () : Void => { void }
property onDecrement : Function(a) = () : Void => { void }
property counter : Number = 0
/* This is the styling for the base div. */
style base {
background: {background};
border-radius: 5px;
transition: 320ms;
display: flex;
padding: 20px;
margin: 20px;
}
/* This is the styling for the counter span. */
style counter {
font-family: sans;
font-size: 20px;
padding: 0 20px;
}
/* This is a computed property for the background color. */
get background : String {
if (counter >= 10) {
"lightgreen"
} else if (counter < 0) {
"orangered"
} else {
"#F2F2F2"
}
}
fun render : Html {
<div::base>
<button onClick={(event : Html.Event) : Void => { onDecrement() }}>
<{ "Decrement" }>
</button>
<span::counter>
<{ Number.toString(counter) }>
</span>
<button onClick={(event : Html.Event) : Void => { onIncrement() }}>
<{ "Increment" }>
</button>
</div>
}
}
To display something on the screen we need to define the Main
component:
component Main {
style base {
justify-content: center;
flex-direction: column;
align-items: center;
font-family: sans;
display: flex;
height: 100vh;
}
fun render : Html {
<div::base>
<Counter/>
</div>
}
}
Comparison
Both implementations follow pretty much the same semantics and look very similar, there are some differences though:
- In JavaScript there are styled elements (different components), in Mint a
style
can be applied to an element individually - In JavaScript values for the styles needs to be passed explicitly, in Mint the
style
blocks use the same scope as functions and computed properties of the component - In JavaScript properties are defined in two blocks, in Mint one-by-one.
- In JavaScript the static CSS is duplicated for each version of the style using different class names (different background color), in Mint there is only one selector using CSS variables
- In JavaScript the text content is implicit, in Mint it is explicit
Statistics
Statistics | JavaScript | Mint |
---|---|---|
Lines of code | 60 | 52 |
Third party packages |
3react react-dom styled-components |
0 |
State
For the state we need a globally accessible entity, which contains the state of the application (counter) and the functions which lets us mutate it, for lack of a better term let's call it a store.
JavaScript
For JavaScript there are a lot of frameworks for handling data in an application using the store paradigm: Redux, Redux Saga, Mobx just to name a few, we are going to use Redux here.
In a new file we create the actions, action creators, reducer and finally the store.
// @flow
import { createStore } from "redux";
/* These are the actions we can take. */
const INCREMENT = "INCREMENT";
const DECREMENT = "DECREMENT";
/* The type of the state. */
type State = {|
counter: number
|};
/* The type of the action. */
type Action = {|
payload: null,
type: string
|};
/* This is the initial state. */
const initialState: State = {
counter: 0
};
/* This is the reducer which steps the state forward. */
const reducer = function(state: State = initialState, action: Action): State {
switch (action.type) {
case INCREMENT:
return { ...state, counter: state.counter + 1 };
case DECREMENT:
return { ...state, counter: state.counter - 1 };
default:
return state;
}
};
/* This is an action creater for the increment action. */
export const increment = (): Action => {
return {
type: INCREMENT,
payload: null
};
};
/* This is an action creater for the decrement action. */
export const decrement = (): Action => {
return {
type: DECREMENT,
payload: null
};
};
/* This is a function which creates a store. */
export const store = createStore(reducer, initialState);
After that we need to connect the store to our component:
...
/* We need to import the action creators and the store from the other file. */
import { increment, decrement } from "./store.jsx";
/* The connect comes from the react-redux package. */
import { connect } from "react-redux";
...
/* This is our main component which is connected to the store. */
class Main extends Component {
render() {
const { counter, onIncrement, onDecrement } = this.props;
return (
<div>
<Counter
onIncrement={onIncrement}
onDecrement={onDecrement}
counter={counter}
/>
</div>
);
}
}
/* We need to map the state from the store to our components properties. */
const mapStateToProps = state => {
return {
counter: state.counter
};
};
/* We need to map the actions from the store to our components properties. */
const mapDispatchToProps = dispatch => {
return {
onIncrement: () => {
dispatch(increment());
},
onDecrement: () => {
dispatch(decrement());
}
};
};
/*
Finally we are creating a new component by connecting the store the original one, using the two functions above.
*/
export const App = connect(
mapStateToProps,
mapDispatchToProps
)(Main);
Mint
In Mint there are only two things we need to do to use a store
:
Declare them:
store Store {
/* The data lives in states. */
state counter : Number = 0
/* A store can have any number of functions. */
fun increment : Promise(Never, Void) {
/* The next statements steps the state forward based on the previous state. */
next { counter = counter + 1 }
}
fun decrement : Promise(Never, Void) {
next { counter = counter - 1 }
}
}
And connect them to a component:
component Main {
/*
We are connecting to the store and explicitly exposing
it's properties and functions to be available for the
component.
*/
connect Store exposing { counter, increment, decrement }
...
fun render : Html {
<div::base>
<Counter
onIncrement={increment}
onDecrement={decrement}
counter={counter}/>
</div>
}
}
Comparison
The basic idea of the two approaches are the same, although the Redux implementation is more complex:
- there are more types of entities (actions, action-creators, store, reducer)
- mapping state and actions to properties
- external functions which needed to be used correctly (
connect
,createStore
) - actions have a specific type, usually with a name
In Mint everything is typed checked even the connection between a component and a store, so for example if we happen to expose something that is not available on the store we get a nice error message.
There are notable difference between the implementations:
- Redux uses the components properties to pass the actions and data, while in Mint it's available in the components scope
- In Redux there is a HOC component which connects the store to the base component
Statistics
Name | JavaScript | Mint |
---|---|---|
Lines of code | 103 | 13 |
Third party packages |
2 redux react-redux
|
0 |
Routing
Our example application for testing purposes should implement three routes:
-
/
to display the counter with the starting value of0
-
/10
(or any number) to display the counter with the starting value which comes from the path -
/about
to display some information about the application (dummy text is enough)
There should be links for all three routes, which the application should handle.
JavaScript
In this case we are going to use react-router.
There are a number of steps we need to take in order to make routing work.
First we need to modify our store to be able to set the count directly:
...
/* Add a new action. */
const SET = "SET";
...
/* Update the Action type. */
export type Action = {|
payload: number | null,
type: string
|};
...
/* Add a new branch in the reducer for the given action. */
case SET:
return { ...state, counter: action.payload || 0 };
...
/* Create an action creator for the new action. */
export const set = (payload : number): Action => {
return {
payload: payload,
type: SET
};
};
Then wen need to create a new component which handles the routing, let's call it Page
/* Import the necessary entitites. */
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
/* Import to store creator. */
import { store } from "./store.jsx";
/*
Create a functional component which parses the count from the route
and passes it to the App component.
*/
const RoutedApp = (props) => {
const parsed = Number.parseInt(props.match.params.count)
const initialCount = parsed || 0
/* The key is needed because the `componentDidMount` function sets the counter. */
return <App
key={initialCount.toString()}
initialCount={ initialCount}
store={store} />
}
/* Create a functional component which has to rooting. */
const Page = () =>
<Router>
<div>
<ul>
<li><Link to="/">0</Link></li>
<li><Link to="/1">1</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
<hr/>
<Route exact path="/about" component={<div></div>}/>
<Route exact path="/" render={RoutedApp}/>
<Route path="/:count" component={RoutedApp}/>
</div>
</Router>
Then we need to modify our App
component to set the counter
when it loads.
/* Expose the set function from the store. */
import { increment, decrement, set } from "./store.jsx";
class Main extends Component {
/* When mounted set the counter. */
componentDidMount () {
this.props.set(this.props.initialCount)
}
...
}
const mapDispatchToProps = dispatch => {
...
/* Add a prop to dispatch the set action. */
set: (payload : number) => {
dispatch(set(payload));
}
}
Mint
First we need to add a function to our store to set the counter
and a state
to denote which page is the active one and a function to set the page
:
store Store {
/* Create a new state for the page. */
state page : String = ""
...
fun setCounter (counter : Number) : Promise(Never, Void) {
next { counter = counter }
}
fun setPage (page : String) : Promise(Never, Void) {
next { page = page }
}
}
Then we handle paths using the routes
top-level block:
/* In a routes block you can define the routes of the application. */
routes {
/*
This matches the /about path, needs to be first because
routes are matched from top to bottom.
*/
/about {
/* We can directly call store functions. */
Store.setPage("about")
}
/* This matches the index path. */
/ {
/* Sequence allows us to do more things in sequence. */
sequence {
Store.setCounter(0)
Store.setPage("counter")
}
}
/* This matches the /10 path. */
/:value (value : String) {
sequence {
/* Here we convert a string to a number safely. */
counter =
value
|> Number.fromString()
|> Maybe.withDefault(0)
Store.setCounter(counter)
Store.setPage("counter")
}
}
}
Then we need to modify our Main component:
component Main {
/* Expose the page property. */
connect Store exposing { counter, increment, decrement, page }
...
get content : Html {
/* Decide what to render based on the page. */
case (page) {
"counter" =>
<Counter
onIncrement={increment}
onDecrement={decrement}
counter={counter}/>
"about" =>
<div>
<{ "about" }>
</div>
=>
<div>
<{ "" }>
</div>
}
}
fun render : Html {
<div::base>
<ul>
<li>
<a href="/">
<{ "/0" }>
</a>
</li>
<li>
<a href="/10">
<{ "/10" }>
</a>
</li>
<li>
<a href="/about">
<{ "/about" }>
</a>
</li>
</ul>
<{ content }>
</div>
}
}
Comparison
The two implementations of the routing is fundamentally different:
- In JavaScript it is component based (render something when the path changes), while in Mint it is action based (do something when the path changes)
- In JavaScript the routing does not interact with the store, in Mint it does
- In JavaScript the routing is handled by the developer, while in Mint it's handled by the runtime
- In JavaScript a component is needed for the links, in Mint they are plain
a
tags and the runtime handles the navigation
Statistics
Name | JavaScript | Mint |
---|---|---|
Lines of code | 68 | 47 |
Third party packages |
1 react-router
|
0 |
Networking
To demonstrate how to fetch something from the network, a simple text files content should be displayed on the about page which is fetched dynamically.
JavaScript
In JavaScript this is straight forward to do using the Fetch API.
We are going to create a stateful component for this:
/* Import React and Component. */
import React, { Component } from "react";
/* The component for the about page. */
export default class About extends Component {
/* In the constructor set the initial state. */
constructor(props) {
super(props)
this.state = {
/* This field is for tracking the status of the request. */
status: "INITIAL",
/* The content which will be displayed once loaded. */
content: ""
}
}
/* When the component is mounted. */
componentDidMount() {
/* Set the status as loading. */
this.setState({ status: "LOADING" }, () => {
/* Fetch the data. */
fetch('/about.txt')
.then((response) => {
/* Get the text. */
response
.text()
.then((body) => {
/* Set the status to loaded and content. */
this.setState({
status: "LOADED",
content: body
})
})
})
.catch(() => {
/* On an error set the status. */
this.setState({
status: "ERRORED",
content: ""
})
})
})
}
render () {
/* Based on the status render things. */
switch (this.state.status) {
case "LOADING":
return <div>Loading...</div>
case "ERRORED":
return <div>Could not load the content...</div>
case "LOADED":
return <div>{this.state.content}</div>
default:
return false
}
}
}
After we have the component we need to update or Page
component to include it:
...
<Route exact path="/about" component={About}/>
...
Mint
In Mint we need to use the Http
module from the standard library mint-core
which is installed automatically on project initialization.
/* Define an enum for the status. */
enum Status {
Initial
Loading
Loaded
Errored
}
/* The component for the about page. */
component About {
/* A state to track the status. */
state status : Status = Status::Initial
/* A state for the content. */
state content : String = ""
/* When the component is mounted. */
fun componentDidMount : Promise(Never, Void) {
/* In a sequence expression statements are executed asynchronously in sequence. */
sequence {
/* Set the status to loading. */
next { status = Status::Loading }
/*
Get the response and unpack it from a
Result(Http.ErrorResponse, Http.Response).
*/
response =
"/about.txt"
|> Http.get()
|> Http.send()
/* Set the status to loaded and the content. */
next
{
status = Status::Loaded,
content = response.body
}
} catch Http.ErrorResponse => error {
/* On an error set the status to errored. */
next
{
status = Status::Errored,
content = ""
}
}
}
/* Renders the component. */
fun render : Html {
/* Renders things based on status. */
case (status) {
Status::Initial => Html.empty()
Status::Loading =>
<div>
<{ "Loading..." }>
</div>
Status::Errored =>
<div>
<{ "Could not load the content..." }>
</div>
Status::Loaded =>
<div>
<{ content }>
</div>
}
}
}
Here also we need to update the Main
component to display it:
...
"about" => <About/>
...
Comparison
The implementation is basically following the same steps but there are differences:
- In JavaScript we can use promises for asynchronous tasks, in Mint it is a language feature using the
sequence
expressions - In JavaScript we can leave out error handling, in Mint it is enforced with nice error messages
- In JavaScript we need to use
this.state
andthis.setState
for state handling, in Mint it is a built in feature using the name of the states andnext
keywords - In JavaScript we need to use strings for the
status
in Mint we can use anenum
Statistics
Statistics | JavaScript | Mint |
---|---|---|
Lines of code | 60 | 72 |
Third party packages | 0 |
1 mint-core
|
Testing
We will write three simple test for the Counter
component:
- displays the counter properly
- clicking on the increment button increments the counter
- clicking on the decrement button decrements the counter
JavaScript
We will use Jest and Enzyme for testing the Counter
component. Also we need to add enzyme-adapter-react-16
for Enzyme to work with React, also we need set some configuration in package.json
for Jest to avoid an error:
...
"jest": {
"testURL": "http://localhost/"
}
}
Now we can create a test file for our component:
/* Import things. */
import Adapter from 'enzyme-adapter-react-16';
import React, { Component } from 'react';
import Enzyme, { mount } from 'enzyme';
/* Configure enzyme. */
Enzyme.configure({adapter: new Adapter()});
/* Import our Counter component. */
import Counter from './counter.jsx';
/* A test component which handles the state. */
class TestComponent extends Component {
constructor(props) {
super(props)
this.state = { counter: 0 }
}
increment() {
this.setState({ counter: this.state.counter + 1 })
}
decrement() {
this.setState({ counter: this.state.counter - 1 })
}
render() {
return <Counter
onIncrement={() => this.increment()}
onDecrement={() => this.decrement()}
counter={this.state.counter}
/>
}
}
it('displays the counter', () => {
const counter = mount(<TestComponent/>);
expect(counter.find('span').text()).toEqual('0');
});
it('decrements the counter', () => {
const counter = mount(<TestComponent/>);
expect(counter.find('span').text()).toEqual('0');
// Simulate a click and update the view.
counter.find('button').first().simulate("click")
counter.update()
expect(counter.find('span').text()).toEqual('-1');
});
it('increments the counter', () => {
const counter = mount(<TestComponent/>);
expect(counter.find('span').text()).toEqual('0');
counter.find('button').last().simulate("click")
counter.update()
expect(counter.find('span').text()).toEqual('1');
});
To run the tests we just run: jest
Mint
In Mint the language has two keywords specifically for testing: suite
and test
, with them we can create tests easily:
/* Create component for testing the counter which contains the state. */
component TestCounter {
state counter : Number = 0
fun render : Html {
<Counter
onIncrement={() : Promise(Never, Void) => { next { counter = counter + 1 } }}
onDecrement={() : Promise(Never, Void) => { next { counter = counter - 1 } }}
counter={counter}/>
}
}
/* A suite is a group of tests. */
suite "Counter" {
test "Displays counter" {
/*
We are using the Test.Html module for testing. The with keyword
allows us to call its functions in the current scope.
*/
with Test.Html {
<TestCounter/>
|> start()
|> assertTextOf("span", "0")
}
}
test "Clicking on increment increments the counter" {
with Test.Html {
<TestCounter/>
|> start()
|> assertTextOf("span", "0")
|> triggerClick("button:last-child")
|> assertTextOf("span", "1")
}
}
test "Clicking on decrement decrements the counter" {
with Test.Html {
<TestCounter/>
|> start()
|> assertTextOf("span", "0")
|> triggerClick("button")
|> assertTextOf("span", "-1")
}
}
}
To run the tests just call the binary with the test command: mint test
Comparison
Both implementations are integration tests:
- In JavaScript to run tests we need third party packages, in Mint it's built in
- In JavaScript Jest runs the tests using nodejs while Mint runs the tests in an actual browsers
- In Mint there is a test server which allows for manually testing in the browser
Statistics
Name | JavaScript | Mint |
---|---|---|
Lines of code | 62 | 47 |
Third party packages |
3 jest enzyme enzyme-adapter-react-16
|
0 |
Error Messages
Our development environment should provide nice easy to understand error messages.
JavaScript
In JavaScript we need to handle three types or errors, of which only one of can be displayed in the browser:
- Compile time errors from Webpack
- Type errors from Flow
- Runtime errors from the browser
To enable compile time errors we need to add the following line to our webpack.config.js
:
...
devServer: {
overlay: true
...
Flow errors can only be displayed in the console after running the binary:
Runtime errors can be seen in the browsers console.
Mint
In Mint there are a number of error types (syntax, type, etc...) but all of them are displayed in the same way either in the console (when running console only commands) or in the browser but with exactly the same content:
Runtime errors can be seen in the browsers console, although they should not happen because of the type system.
Comparison
Mint errors tend to be more informative for example when miscalling a function the message shows the where the function is called and it's source.
Formatting
It is a standard practice to format our source code to a specific style, our environment should support that.
JavaScript
To format our JavaScript files we only need to install Prettier which can handle a number of languages not just JavaScript.
After installing we only need to call prettier src/* --write
and our code is formatted in place.
Mint
Mint has a built in formatter which can be invoked with the mint format
command, also the development server can be configured to format files when they change with the --auto-format
argument.
Comparison
It's equally simple to format our code in both languages the only difference is that in JavaScript it's a third party tool.
Statistics
Name | JavaScript | Mint |
---|---|---|
Lines of code | 0 | 0 |
Third party packages |
1 prettier
|
0 |
Building production files
Our application is ready to be deployed to production, but for that we need to produce compressed and minified files. Also it would be nice to generate favicons from a base icon.
JavaScript
To minify our JavaScript output we will use UglifyJs via the uglifyjs-webpack-plugin
plugin. To generate favicons we need to install the html-webpack-plugin
and favicons-webpack-plugin
plugins.
After installing them it we need to configure them in our webpack.config.js
file:
/* Import them. */
const FaviconsWebpackPlugin = require('favicons-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
...
plugins: [
new FaviconsWebpackPlugin('../public/logo.png'),
new HtmlWebpackPlugin({
template: '../public/index.html'
})
],
optimization: {
minimizer: [
new UglifyJsPlugin()
]
}
}
Also we need to create a new configuration for the build webpack.prod.config.js
:
const common = require('./webpack.config.js');
module.exports = {
...common,
mode: 'production'
}
After that can just call webpack --config webpack.prod.config.js
to build our production files into the dist
directory.
Mint
Currently Mint does not minifies the production files, although it can be minified with any tool out there. It is planned to be implemented soon.
To generate favicons, we only need to specify the icon for the application (this feature requires ImageMagick to be installed):
...
"application": {
...
"icon": "assets/logo.png"
}
...
Then we can generate the production files to the dist
directory with the mint build
command.
Bonus: Progressive Web Application
Mint by default generates the manifest.json
and a service worker for all applications, all we need to do is set the corresponding fields in the mint.json
file:
{
...
"application": {
...
"icon": "assets/logo.png",
"name": "Counter",
"orientation": "portrait",
"display": "standalone",
"theme-color": "#FFF",
"meta": {
"viewport": "width=device-width, initial-scale=1, shrink-to-fit=no",
"description": "A simple counter example",
"charset": "utf-8"
}
}
...
}
After building it again and deploying it to a secure website (with https) it is installable on mobile phones.
Comparison
Using JavaScript third party tools are needed for building the production files, in Mint it's a built in feature.
Statistics
Name | JavaScript | Mint |
---|---|---|
Lines of code | 14 | 1 |
Third party packages |
3 html-webpack-plugin uglifyjs-webpack-plugin favicons-webpack-plugin
|
0 |
Overall Statistics
Here you can find the final statistics of the two implementations (all of them are collected on a single machine during the same conditions):
Name | JavaScript | Mint |
---|---|---|
Lines of code (wc -l ) |
408 | 258 |
Production build time | 21.36 s | 854 ms |
Command line utilities used | 6 | 1 |
Third party packages | 24 | 1 |
All installed packages | 1426 | 1 |
Packages size (node_modules / .mint ) |
296 MB | 744 kB |
Bundle Size (dist directory) |
1.3 MB | 315 kB |
Bundle Size (.js ) |
212 kB (minified) |
204 kB (unminifed) 176 kB (minified) |
As you can see above the main difference is in the third party packages and packages size. In JavaScript it's bigger because it contains the tools as well.
Ending Thoughts
This part is probably subjective (since I'm the author of Mint) so take it as is.
In my opinion this really shows how over engineered todays front-end development is (1426 packages for such a simple application??). Also it's not enough that a developer needs to learn basics of web development (HTML, CSS, JavaScript) they need to learn all of these dependencies as well, which come with their own documentation and that can be overwhelming.
This is basically why I created Mint so it would be easier to write web applications without all the hassle. I hope this article shows what Mint can do and how easy it is to use it.
If I piqued your interest you can find all the information to get started on the website or if you like to contribute check out the Github repository:
A refreshing programming language for the front-end web, aiming to solve the most common issues of Single Page Applications (SPAs) at a language level:
- Reusable components
- Styling
- Routing
- Global and local state handling
- Synchronous and asynchronous computations that might fail
While focusing on:
- Developer happiness
- Fast compilation
- Readability
Project Status
The project is in development, we are still tweaking the language and standard library.
There are some bigger applications which can be used as examples / learning material:
- Mint Realworld (~3300 LOC)
- Mint UI (~9500 LOC)
- Mint UI Website (~27256 LOC)
Installing
Documentation
Community
Questions or suggestions? Ask on Discord
Also, visit Awesome Mint, to see more guides, tutorials and examples.
Contributing
Read the general Contributing guide, and then:
- Fork it (https://github.com/mint-lang/mint/fork)
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit
…
The full code for both applications can be found here (on separate branches):
Mint Vs. X
This repository contains the implementation of a sample application which is used in "Mint vs X" blog posts of comparing Mint with popular frontend frameworks / languages
Implementations
All of the implementation lives in separate branches:
- Mint - The base implementation in Mint
- JavaScript - A JavaScript implementation using React + Redux
Blog Posts
Also if you are interested in more applications written in Mint I suggest you check out these repositories:
mint-lang / mint-website
The website of the Mint programming language
mint-lang / mint-realworld
Mint implementation of https://realworld.io frontend
Mint Realworld
Mint codebase containing real world examples (CRUD, auth, advanced patterns, etc) that adheres to the RealWorld spec and API.
This codebase was created to demonstrate a fully fledged fullstack application built with Mint including CRUD operations, authentication, routing, pagination, and more.
We've gone to great lengths to adhere to the Mint community styleguides & best practices.
For more information on how to this works with other frontends/backends, head over to the RealWorld repo.
How it works
This implemenation only uses the Mint language and it's standard library without any third party dependencies.
To learn more about Mint check out the Guide
Differences
There are a few difference to other implementations:
- since Mint has a built in way of styling HTML elements we wanted to showcase that, so the design of the application greatly differs from the original one
- the end result is also a Progressive Web…
Top comments (0)