Skip to content

Commit 6dd4a67

Browse files
authored
Merge pull request nullstack#345 from nullstack/next
Next
2 parents 1be78bb + 91fcd97 commit 6dd4a67

38 files changed

+469
-68
lines changed

.github/workflows/pr-tests.yml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@ jobs:
2121

2222
steps:
2323
- uses: actions/checkout@v2
24+
- uses: pnpm/action-setup@v2
25+
with:
26+
version: 7
2427
# cache the dependencies from any node_modules directory
2528
- name: Cache dependencies
2629
uses: actions/cache@v2
2730
with:
2831
path: |
2932
**/node_modules
30-
**/package-lock.json
33+
**/pnpm-lock.yaml
3134
key: node_modules-${{ matrix.node-version }}-${{ hashFiles('**/package.json') }}
3235

3336
- name: Use Node.js ${{ matrix.node-version }}
@@ -37,15 +40,15 @@ jobs:
3740

3841
- name: Install and link main deps
3942
run: |
40-
npm install
41-
npm link
43+
pnpm install
44+
pnpm link --global
4245
4346
- name: Install deps at tests folder
4447
working-directory: ./tests
4548
run: |
46-
npm link nullstack
47-
npm install
49+
pnpm link nullstack --global
50+
pnpm install
4851
4952
- name: Run tests
5053
working-directory: ./tests
51-
run: npm test
54+
run: pnpm test

client/client.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ client.selector = null
1919
client.events = {}
2020
client.generateContext = generateContext
2121
client.renderQueue = null
22-
client.currentBody = {}
23-
client.nextBody = {}
22+
client.currentMeta = { body: {}, html: {}, window: {} }
23+
client.nextMeta = { body: {}, html: {}, window: {} }
2424
client.currentHead = []
2525
client.nextHead = []
2626
client.head = document.head

client/events.js

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,19 @@ export const eventCallbacks = new WeakMap()
66
export const eventSubjects = new WeakMap()
77
export const eventDebouncer = new WeakMap()
88

9-
function executeEvent(callback, subject, event, data) {
10-
if (typeof callback === 'object') {
11-
Object.assign(subject.source, callback)
12-
} else {
13-
callback({ ...subject, event, data })
9+
export function generateSubject(selector, attributes, name) {
10+
if (Array.isArray(attributes[name])) {
11+
for (let i = 0; i < attributes[name].length; i++) {
12+
if (typeof attributes[name][i] === 'object') {
13+
let changeset = attributes[name][i]
14+
attributes[name][i] = () => Object.assign(attributes.source, changeset)
15+
}
16+
}
17+
} else if (typeof attributes[name] === 'object') {
18+
let changeset = attributes[name]
19+
attributes[name] = () => Object.assign(attributes.source, changeset)
1420
}
21+
eventSubjects.set(selector, attributes)
1522
}
1623

1724
function debounce(selector, name, time, callback) {
@@ -60,17 +67,23 @@ export function generateCallback(selector, name) {
6067
object[property] = event.target[valueName] === 'true'
6168
} else if (typeof object[property] === 'number') {
6269
object[property] = +event.target[valueName] || 0
70+
} else if (object[property] instanceof Date) {
71+
const [yyyy, mm, dd] = event.target[valueName].split('-')
72+
object[property].setFullYear(yyyy)
73+
object[property].setMonth(+mm - 1)
74+
object[property].setDate(dd)
75+
object[property] = object[property]
6376
} else {
6477
object[property] = event.target[valueName]
6578
}
6679
}
6780
if (subject[name] === noop) return
6881
if (Array.isArray(subject[name])) {
6982
for (const subcallback of subject[name]) {
70-
executeEvent(subcallback, subject, event, data)
83+
subcallback({ ...subject, event, data })
7184
}
7285
} else {
73-
executeEvent(subject[name], subject, event, data)
86+
subject[name]({ ...subject, event, data })
7487
}
7588
})
7689
}

client/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,9 @@ export default class Nullstack {
6868
} else {
6969
client.virtualDom = await generateTree(client.initializer(), scope)
7070
hydrate(client.selector, client.virtualDom)
71-
client.currentBody = client.nextBody
71+
client.currentMeta = client.nextMeta
7272
client.currentHead = client.nextHead
73-
client.nextBody = {}
73+
client.nextMeta = { body: {}, html: {}, window: {} }
7474
client.nextHead = []
7575
context.environment = environment
7676
scope.plugins = loadPlugins(scope)

client/render.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { sanitizeInnerHtml } from '../shared/sanitizeString'
22
import generateTruthyString from '../shared/generateTruthyString'
33
import { isFalse, isText } from '../shared/nodes'
44
import { anchorableElement } from './anchorableNode'
5-
import { eventSubjects, generateCallback } from './events'
5+
import { generateCallback, generateSubject } from './events'
66
import { ref } from './ref'
77

88
export default function render(node, options) {
@@ -36,7 +36,7 @@ export default function render(node, options) {
3636
const eventName = name.substring(2)
3737
const callback = generateCallback(node.element, name)
3838
node.element.addEventListener(eventName, callback)
39-
eventSubjects.set(node.element, node.attributes)
39+
generateSubject(node.element, node.attributes, name)
4040
}
4141
} else {
4242
let nodeValue

client/rerender.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import generateTruthyString from '../shared/generateTruthyString'
33
import { isFalse, isText, isUndefined } from '../shared/nodes'
44
import { anchorableElement } from './anchorableNode'
55
import client from './client'
6-
import { eventCallbacks, eventSubjects, generateCallback } from './events'
6+
import { eventCallbacks, eventSubjects, generateCallback, generateSubject } from './events'
77
import { reref } from './ref'
88
import render from './render'
99

@@ -36,6 +36,7 @@ function updateAttributes(selector, currentAttributes, nextAttributes) {
3636
if (!callback) {
3737
selector.addEventListener(eventName, generateCallback(selector, name))
3838
}
39+
generateSubject(selector, nextAttributes, name)
3940
eventSubjects.set(selector, nextAttributes)
4041
}
4142
}
@@ -156,12 +157,14 @@ function _rerender(current, next) {
156157

157158
export default function rerender() {
158159
_rerender(client.virtualDom, client.nextVirtualDom)
159-
updateAttributes(client.body, client.currentBody, client.nextBody)
160+
updateAttributes(client.body, client.currentMeta.body, client.nextMeta.body)
161+
updateAttributes(window, client.currentMeta.window, client.nextMeta.window)
162+
updateAttributes(client.body.parentElement, client.currentMeta.html, client.nextMeta.html)
160163
updateHeadChildren(client.currentHead, client.nextHead)
161164
client.virtualDom = client.nextVirtualDom
162165
client.nextVirtualDom = null
163-
client.currentBody = client.nextBody
164-
client.nextBody = {}
166+
client.currentMeta = client.nextMeta
167+
client.nextMeta = { body: {}, html: {}, window: {} }
165168
client.currentHead = client.nextHead
166169
client.nextHead = []
167170
}

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "nullstack",
3-
"version": "0.18.0",
4-
"description": "Full Stack Javascript Components for one-dev armies",
3+
"version": "0.19.0",
4+
"description": "Feature-Driven Full Stack JavaScript Components",
55
"main": "./types/index.d.ts",
66
"author": "Mortaro",
77
"repository": "github:nullstack/nullstack",
@@ -32,7 +32,7 @@
3232
"css-loader": "6.7.3",
3333
"css-minimizer-webpack-plugin": "^4.2.2",
3434
"dotenv": "16.0.3",
35-
"eslint-plugin-nullstack": "0.0.12",
35+
"eslint-plugin-nullstack": "0.0.26",
3636
"express": "4.18.2",
3737
"fs-extra": "11.1.0",
3838
"lightningcss": "^1.19.0",
@@ -43,7 +43,7 @@
4343
"sass-loader": "13.2.0",
4444
"style-loader": "^3.3.1",
4545
"swc-loader": "0.2.3",
46-
"swc-plugin-nullstack": "0.1.2",
46+
"swc-plugin-nullstack": "0.1.3",
4747
"terser-webpack-plugin": "5.3.6",
4848
"time-analytics-webpack-plugin": "^0.1.20",
4949
"webpack": "^5.0.0",

plugins/bindable.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ function transform({ node, environment }) {
1212
node.children = [object[property] ?? '']
1313
} else if (node.type === 'input' && node.attributes.type === 'checkbox') {
1414
node.attributes.checked = object[property]
15+
} else if (node.type === 'input' && node.attributes.type === 'date' && object[property] instanceof Date) {
16+
const yyyy = object[property].getFullYear()
17+
const mm = (object[property].getMonth() + 1).toString().padStart(2, '0')
18+
const dd = object[property].getDate().toString().padStart(2, '0')
19+
node.attributes.value = `${yyyy}-${mm}-${dd}`
1520
} else {
1621
node.attributes.value = object[property] ?? ''
1722
}

server/prerender.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export async function prerender(request, response) {
3131
scope.body = ''
3232
scope.context = context
3333
scope.generateContext = generateContext(context)
34-
scope.nextBody = {}
34+
scope.nextMeta = { body: {}, html: {}, window: {} }
3535
scope.nextHead = []
3636
scope.plugins = loadPlugins(scope)
3737

@@ -48,7 +48,7 @@ export async function prerender(request, response) {
4848
context.page.status = 500
4949
} finally {
5050
if (context.page.status !== 200) {
51-
scope.nextBody = {}
51+
scope.nextMeta = {body: {}, html: {}, window: {}}
5252
scope.nextHead = []
5353
for (const key in context.router._routes) {
5454
delete context.router._routes[key]

server/template.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import project from './project'
66
import renderAttributes from './renderAttributes'
77
import settings from './settings'
88

9-
export default function ({ head, body, nextBody, context, instances }) {
9+
export default function ({ head, body, nextMeta, context, instances }) {
1010
const { page, router, worker, params } = context
1111
const canonical = absolute(page.canonical || router.url)
1212
const image = cdnOrAbsolute(page.image)
@@ -39,7 +39,7 @@ export default function ({ head, body, nextBody, context, instances }) {
3939
context: environment.mode === 'spa' ? {} : serializableContext,
4040
}
4141
return `<!DOCTYPE html>
42-
<html lang="${page.locale || ''}">
42+
<html lang="${page.locale || ''}" ${renderAttributes(nextMeta.html)}>
4343
<head>
4444
<meta charset="utf-8">
4545
<meta name="generator" content="Created with Nullstack - https://nullstack.app" />
@@ -75,7 +75,7 @@ export default function ({ head, body, nextBody, context, instances }) {
7575
integrities['client.js'] || ''
7676
}" defer crossorigin="anonymous"></script>
7777
</head>
78-
<body ${renderAttributes(nextBody)}>
78+
<body ${renderAttributes(nextMeta.body)}>
7979
${environment.mode === 'spa' ? '<div id="application"></div>' : body}
8080
</body>
8181
</html>`

0 commit comments

Comments
 (0)