Skip to content

Commit 79d3789

Browse files
committed
first commit
0 parents  commit 79d3789

28 files changed

+2313
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.DS_Store
2+
pnpm-lock.yaml
3+
node_modules
4+
dist

CONTRIBUTING.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
2+
3+
# Contributing to Nue
4+
Nues's codebase has two distinct parts:
5+
6+
* The reactive client is under [src](src) directory
7+
* Server parts are under [ssr](ssr) directory (SSR: "Server Side Rendering")
8+
9+
[Bun](//bun.sh) is the preferred test and development environment because it's noticeably faster than Node or Deno.
10+
11+
12+
## Best practises
13+
14+
1. If you are adding a new feature, please add a test. If you are fixing a bug, please add a test that fails before your fix and passes after your fix.
15+
16+
2. Nue is a minimalistic project. The goal is to stay lean in all areas of development. Keep things simple and depend on as little dependencies as possible.
17+
18+
3. JavaScript is preferred over TypeScript because of minimalism, dynamic typing and it's standards-based. TypeScript should be used if there is a clear use case or a problem it solves.
19+
20+
4. New features add complexity. They must always be discussed before implemented.
21+
22+
### Running server test
23+
Nue uses [Bun](//bun.sh) for running tests:
24+
25+
1. Go to root directory: `cd nuejs`
26+
2. Run `bun test`
27+
28+
29+
### Running client tests
30+
31+
1. Go to root directory: `cd nuejs`
32+
2. Start a web server, for example: `python -m SimpleHTTPServer`
33+
2. Open a test page. Either [basics][basics] or [loops][loops]
34+
35+
[basics]: http://localhost:8000/test/client/basics.html
36+
[loops]: http://localhost:8000/test/client/loops.html
37+
38+
39+
## FAQ
40+
41+
### Why not the `===` operator? [equality]
42+
Strict typing is rarely needed in dynamically typed languages. Loose typing (`==`) is usually enough.
43+
44+
45+
### Why not semicolons or double quotes?
46+
Semicolons are optional and double is literally 2x. Less is more.
47+
48+
### Why not TypeScript?
49+
This is answered on our [general FAQ page](//nuejs.org/faq/#ts)
50+
51+

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2023-present, Tero Piirainen
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
2+
3+
![Nue logo](https://nuejs.org/global/logo/logo.png)
4+
5+
6+
[Documentation](//nuejs.org/docs/nuejs/)
7+
[Backstory](//nuejs.org/backstory/)
8+
[Ecosystem](//nuejs.org/ecosystem/)
9+
10+
11+
# What is Nue?
12+
13+
Nue JS is an exceptionally small (2.3kb minzipped) JavaScript library for building web interfaces. It is the core of the upcoming [Nue ecosystem](//nuejs.org/ecosystem/). It’s like **Vue.js, React.js**, or **Svelte** but there are no hooks, effects, props, portals, watchers, provides, injects, suspension, or other internal abstractions on your way. Learn the basics of HTML, CSS, and JavaScript and you are good to go.
14+
15+
Maybe the biggest benefit is that you need less code to do the same thing:
16+
17+
![The amount of code required to build a basic listbox UI component](https://nuejs.org/docs/img/react-listbox-big.jpg)
18+
19+
20+
21+
It's not unusual to see 10x differences in the amount of code you need to write. For example, a listbox component written with Nue is around [ten times smaller](//nuejs.org/compare/component) than the [React version](https://headlessui.com/react/listbox) from the Headless UI project.
22+
23+
24+
## "It's just HTML"
25+
Nue uses an HTML-based template syntax:
26+
27+
``` html
28+
<div @name="media-object" class="{ type }">
29+
<img src="{ img }">
30+
<aside>
31+
<h3>{ title }</h3>
32+
<p :if="desc">{ desc }</h3>
33+
<slot/>
34+
</aside>
35+
</div>
36+
```
37+
38+
While React and JXS claim to be "Just JavaScript", Nue can be thought of as "Just HTML". Nue is perfect for [UX developers][divide] focusing on interaction design, accessibility, and user experience.
39+
40+
41+
## Built to scale
42+
Tree reasons why Nue scales extremely well:
43+
44+
1. [Separation of concerns](//nuejs.org//why/#soc), easy-to-understand code is easier to scale than "spaghetti code"
45+
46+
1. [Minimalism](//nuejs.org/why/#minimalism), a hundred lines of code is easier to scale than a thousand lines of code
47+
48+
1. **Separation of talent**, when UX developers focus on the [front of the frontend][back] and JS/TS developers focus on the back of the frontend your team skills are optimally aligned:
49+
50+
![The best results are gained when UX developers and JavaScript developers work together without overlaps](https://nuejs.org/docs/img/ux-developer-big.png)
51+
52+
53+
### Decoupled styling
54+
Nue does not promote the use of Scoped CSS, style attribute, Tailwind, or other CSS-in-JS gymnastics:
55+
56+
1. **More reusable code**: When styling is not hardcoded to the component, the same component can look different depending on the page or context.
57+
58+
1. **No spaghetti code**: pure HTML or pure CSS is easier to read than mixed spaghetti code
59+
60+
1. **Faster page loads**: With decoupled styling it's easier to extract primary CSS from the secondary and keep your HTML page under the critical [14kb limit][fourteen].
61+
62+
Learn more about [styling](//nuejs.org/docs/styling-components)
63+
64+
65+
## Reactive and isomorphic
66+
Nue has a rich component model and it allows you to create all kinds of applications using different kinds of components:
67+
68+
1. [Server components](//nuejs.org/docs/server-components) are rendered on the server. They help you build content-focused websites that load faster without JavaScript and are crawlable by search engines.
69+
70+
2. [Reactive components](//nuejs.org/docs/reactive-components) are rendered on the client. They help you build dynamic islands or single-page applications.
71+
72+
3. [Hybrid components](//nuejs.org/docs/isomorphic-components#hybrid) are partly rendered on the server side, and partly on the client side. These components help you build reactive, SEO-friendly components like video tags or image galleries.
73+
74+
3. [Universal components](//nuejs.org/docs/isomorphic-components#universal-components) are used identically on both server- and client side.
75+
76+
77+
78+
## UI library files
79+
Nue allows you to define multiple components on a single file. This is a great way to group related components together and simplify dependency management.
80+
81+
82+
``` html
83+
<!-- shared variables and methods -->
84+
<script>
85+
import { someMethod } from './util.js'
86+
</script>
87+
88+
<!-- first component -->
89+
<article @name="todo">
90+
...
91+
</article>
92+
93+
<!-- second component -->
94+
<div @name="todo-item">
95+
...
96+
</div>
97+
98+
<!-- third component -->
99+
<time @name="cute-date">
100+
...
101+
</time>
102+
```
103+
104+
With library files, your filesystem hierarchy looks cleaner and you need less boilerplate code to tie connected pieces together. They help in packaging libraries for others.
105+
106+
107+
## Simpler tooling
108+
Nue JS comes with a simple `render` function for server-side rendering and a `compile` function to generate components for the browser. You don't need complex bundlers like Webpack or Vite to take control of your development environment. Just import Nue to your project and you are good to go.
109+
110+
You can of course use a bundler on the business model if your application becomes more complex with tons of dependencies. [Bun](//bun.sh) and [esbuild](//esbuild.github.io/) are great, performant options.
111+
112+
113+
## Use cases
114+
Nue JS is a versatile tool that supports both server- and client-side rendering and helps you build both content-focused websites and reactive single-page applications.
115+
116+
1. **UI library development** Create reusable components for reactive frontends or server-generated content.
117+
118+
2. **Progressive enhancement** Nue JS is a perfect micro library to enhance your content-focused website with dynamic components or "islands"
119+
120+
3. **Static website generators** Just import it into your project and you are ready to render. No bundlers are needed.
121+
122+
4. **Single-page applications** Build simpler and more scalable apps together with an upcoming *Nue MVC*- project.
123+
124+
5. **Templating** Nue is a generic tool to generate your websites and HTML emails.
125+
126+
127+
[fourteen]: https://developer.mozilla.org/en-US/docs/Web/Performance/How_browsers_work#tcp_slow_start_14kb_rule
128+
129+
[divide]: https://css-tricks.com/the-great-divide/
130+
131+
[back]: https://bradfrost.com/blog/post/front-of-the-front-end-and-back-of-the-front-end-web-development/
132+
133+

package.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "nuejs",
3+
"type": "module",
4+
"main": "ssr/index.js",
5+
"version": "0.1.0",
6+
7+
"scripts": {
8+
"test": "cd test && bun test",
9+
"minify": "bun scripts/minify.js",
10+
"compile": "cd test && bun compile.js"
11+
},
12+
13+
"dependencies": {
14+
"htmlparser2": "^9.0.0"
15+
}
16+
17+
}

scripts/minify.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env node
2+
3+
/*
4+
Minifies source files into a single executable: dist/nue.js
5+
*/
6+
7+
import { promises as fs } from 'node:fs'
8+
import { join } from 'node:path'
9+
10+
11+
try {
12+
let Bundler = process.isBun ? Bun : await import('esbuild')
13+
14+
// recursive forces creation
15+
await fs.mkdir('dist', { recursive: true })
16+
17+
// minify (with Bun or esbuild)
18+
await Bundler.build({
19+
entryPoints: [join('src', 'nue.js')],
20+
outdir: 'dist',
21+
format: 'esm',
22+
minify: true,
23+
bundle: true,
24+
})
25+
26+
console.log('Minified Nue to dist/nue.js with', process.isBun ? 'Bun' : 'ESBuild')
27+
28+
} catch (e) {
29+
console.log('No bundler found. Please install Bun or ESbuild')
30+
31+
}

src/for.js

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
2+
import createApp from './nue.js'
3+
4+
export default function(opts) {
5+
const { root, fn, fns, deps, ctx } = opts
6+
var anchor, current, items, $keys, $index, is_object_loop, blocks = []
7+
8+
9+
function createProxy(item) {
10+
return new Proxy({}, {
11+
get(__, key) {
12+
if (is_object_loop) {
13+
const i = $keys.indexOf(key)
14+
if (i >= 0) return item[i]
15+
}
16+
return key === $keys ? item :
17+
$keys.includes(key) ? item[key] :
18+
key == $index ? items.indexOf(item) :
19+
ctx[key]
20+
}
21+
})
22+
}
23+
24+
function mountItem(item, i, arr, first) {
25+
const block = createApp({ fns, dom: root.cloneNode(true) }, createProxy(item), deps, ctx)
26+
27+
blocks[first ? 'unshift' : 'push'](block)
28+
block.before(first || anchor)
29+
30+
// on insert (anomation)
31+
ctx.oninsert?.call(ctx, block.$el, item, {
32+
index: i,
33+
is_repaint: !!arr,
34+
is_first: !i,
35+
is_last: i == items.length -1,
36+
items
37+
})
38+
39+
}
40+
41+
function repaint() {
42+
blocks.forEach(el => el.unmount())
43+
blocks = []
44+
items.forEach(mountItem)
45+
}
46+
47+
function arrProxy(arr) {
48+
const { unshift, splice, push, sort, reverse } = arr
49+
50+
return Object.assign(arr, {
51+
52+
// adding
53+
push(item) {
54+
push.call(items, item)
55+
mountItem(item, items.length - 1)
56+
},
57+
58+
unshift(item) {
59+
unshift.call(items, item)
60+
mountItem(item, 0, null, blocks[0].$el)
61+
},
62+
63+
// sorting
64+
sort(fn) {
65+
sort.call(items, fn)
66+
repaint()
67+
},
68+
69+
reverse() {
70+
reverse.call(items)
71+
repaint()
72+
},
73+
74+
// removing
75+
splice(i, len) {
76+
blocks.slice(i, i + len).forEach(el => el.unmount())
77+
blocks.splice(i, len)
78+
splice.call(items, i, len)
79+
},
80+
81+
shift() { arr.splice(0, 1) },
82+
83+
pop() { arr.splice(arr.length -1, 1) },
84+
85+
// handy shortcut for a common operation
86+
remove(item) {
87+
const i = items.indexOf(item)
88+
if (i >= 0) arr.splice(i, 1)
89+
}
90+
})
91+
}
92+
93+
// update function
94+
function update() {
95+
var arr
96+
[$keys, arr, $index, is_object_loop] = fn(ctx)
97+
98+
if (items) {
99+
// change of current array --> repaint
100+
if (arr !== current) {
101+
items = arrProxy(arr); repaint(); current = arr
102+
}
103+
return blocks.forEach(el => el.update())
104+
}
105+
106+
if (arr) {
107+
// anchor
108+
const p = root.parentElement
109+
anchor = new Text('')
110+
p.insertBefore(anchor, root)
111+
112+
p.removeChild(root)
113+
items = arrProxy(arr)
114+
arr.forEach(mountItem)
115+
current = arr
116+
}
117+
}
118+
119+
return { update }
120+
121+
}

0 commit comments

Comments
 (0)