Skip to content

Commit 6026d1c

Browse files
Merge branch 'release/v0.17.2'
2 parents 77b4927 + f493844 commit 6026d1c

9 files changed

Lines changed: 1297 additions & 3298 deletions

File tree

.eslintrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"root": true,
23
"env": {
34
"browser": true,
45
"commonjs": true,

.github/workflows/build.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ jobs:
2222
node-version: 18
2323
- run: npm ci
2424
- run: npm run build
25-
- run: npm run prod
2625
- name: Archive production artifacts
2726
uses: actions/upload-artifact@v2
2827
with:

.github/workflows/release.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ jobs:
2121
node-version: 18
2222
- run: npm ci
2323
- run: npm run build
24-
- run: npm run prod
2524

2625
publish-npm:
2726
needs: build
@@ -34,7 +33,6 @@ jobs:
3433
registry-url: https://registry.npmjs.org/
3534
- run: npm ci
3635
- run: npm run build
37-
- run: npm run prod
3836
- run: npm publish
3937
env:
4038
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
@@ -50,7 +48,6 @@ jobs:
5048
registry-url: https://registry.npmjs.org/
5149
- run: npm ci
5250
- run: npm run build
53-
- run: npm run prod
5451
- uses: softprops/action-gh-release@v1
5552
if: startsWith(github.ref, 'refs/tags/')
5653
with:

README.md

Lines changed: 113 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -128,68 +128,92 @@ Also EFML is the first language that can be parsed into the AST which ef support
128128
Here is an example.
129129

130130
```efml
131-
Tree structure
132-
Lines not started with >#%@.|+- are considered as comments
133-
The escape character of EFML is '&', for prevention of conflicts with js escapes.
134-
Except for changes of the characters, all the usage should remain the same on all versions.
135-
this is a comment
131+
Lines not start with >#%@.|+- are comments.
132+
For example, this line is a comment.
133+
The escape character of EFML is `&`, for prevention of conflicts with js escapes.
136134
137-
Lines starting with '>' stand for a new tag
135+
Lines starting with '>' stand for a new tag, for example, a `div` tag can be written as:
138136
>div
139-
Lines with exactly one indent after a tag definition are considered to be all things belongs to the defined tag
137+
138+
`.` following a tag definition is the short hand for writing class names, for example:
139+
>div.my.class.name
140+
would be rendered as `<div class="my class name">`
141+
142+
Class names can have mustaches mixed together:
143+
>div.my.{{dynamic}}.class.name
144+
would be rendered as `<div class="my class name">` when `instance.$data.dynamic` is empty, `<div class="my dynamic class name">` when `instance.$data.dynamic` is 'dynamic'
145+
146+
`#` at the end of a tag definition means the reference name for this element, for example:
147+
>div#myDiv
148+
this `div` tag is now accessable form `instance.$refs.myDiv`.
149+
150+
New lines with exactly one indent after a tag definition are all things that belong to the defined tag, for example:
151+
>div
152+
#attribute = myAttr
153+
%property = myProp
154+
@event = myEventHandler
155+
>ChildElement
140156
141157
Lines starting with '#' stand for attributes
142-
Mustaches are used for binding data
143-
Contents inside mustaches after '=' stand for the default value for this binding
144-
Contents without mustaches stand for static data,
145-
which means that you can not modify them through ef.js
146-
#class = {{class = some class name}}
147-
#style = {{attr.style = background: #ECECEC}}
148-
#id = testdiv
158+
159+
This means an attribute without any parameters:
160+
#flagattr
161+
162+
Contents without mustaches stand for static data:
163+
#id = myID
149164
#some-attr = some text
150-
#content
151165
152-
Lines starting with '%' stand for properties
166+
Mustaches are used for binding data:
167+
#class = {{myClass}}
168+
The element's class name can then be set via `instance.$data.myClass = 'some class names'`
169+
170+
Contents inside mustaches after '=' stand for the default value for this binding:
171+
#class = {{myClass = some class name}}
172+
#style = {{attr.style = background: #ECECEC}}
173+
174+
Static content and mustaches can be mixed:
175+
#class = static and {{mixed}} classes
176+
177+
Lines starting with '%' stand for properties that can be accessed from the DOM object
153178
%title = Welcome, {{name}}
154179
%anotherProperty = text
155180
156-
Lines starting with '@' stand for events
157-
Contents after ':' are considered as value to be passed to the handler
181+
Lines starting with '@' stand for events that triggers an event handler method:
182+
@click = clickHandler
183+
184+
Contents after ':' are values to be passed to the handler:
158185
@click = updateInfo:{{binding.value}} and static value
159-
modifier keys now can bind easily
186+
187+
modifier keys now can bind with predefined aliases:
160188
@mousedown.shift.alt.ctrl.meta = select
161-
bind to keys is also easy
189+
190+
bind to specific key code by writing the code directely:
162191
@keypress.13 = submit
163-
use '.prevent' to `preventDefault`, '.stop' to `stopPropagation`, '.stopImmediate' to `stopImmediatePropagation`
192+
193+
use '.prevent' for `preventDefault`, '.stop' for `stopPropagation`, '.stopImmediate' for `stopImmediatePropagation`
164194
use '.passive' to make the event listener passive, '.!passive' to explicitly claim a non-passive listener
165195
use '.once' to create a trigger once listener
166196
@keydown.8.prevent.stop = stopbackspace
197+
167198
use '.capture' to capture an event
168199
@submit.capture.stopImmediate = submit
169200
170201
Lines starting with '.' stand for text nodes
171202
.Name: {{name}}&nJob: {{job}}
203+
172204
>pre
173205
Lines starting with '|' stand for multiline text
174206
|Line 1
175207
|Line 2
176208
|Line 3
177-
>br
178209
179210
Lines starting with '-' stand for single node mounting point
180211
-node1
212+
use `instance.node1 = anotherInstance` to put another EF component right at the point.
181213
182214
Lines starting with '+' stand for multi node mounting point
183215
+list1
184-
185-
'.' after a tag name stand for class names for this tag
186-
>p.some.{{binding.class}}.class.names
187-
188-
'#' at the end of a tag name stand for the reference name of the node
189-
Mustaches after a dot will bind to 'class' automatically
190-
>span.{{emergency = emergency}}#notice_box
191-
.Notice: {{notice}}
192-
.some text
216+
use `instance.list1.push(...newInstances)` to put other EF components here.
193217
```
194218

195219
For standalone eft parser see [eft-parser](https://github.com/ClassicOldSong/eft-parser).
@@ -230,7 +254,7 @@ ef.toEFComponent(Any)
230254
```
231255
### Attribute Mapping
232256

233-
Data on ef.js components are not always that easy to access, so since v0.10.4, a stable version of attribute mapping helper is bundled with ef.js. For documents, please refer to the [comments](https://github.com/TheNeuronProject/ef-core/blob/master/src/lib/map-attrs.js#L50-L67) for now. It would be extremely useful when using with custom components and JSX.
257+
Data on ef.js components are not always that easy to access, so since v0.10.4, a stable version of attribute mapping helper is bundled with ef.js. For documents, please refer to the [comments](https://github.com/TheNeuronProject/ef-core/blob/master/src/lib/map-attrs.js#L50-L67) for now. It would be extremely useful when using with custom components.
234258

235259
## Custom Components
236260

@@ -521,52 +545,73 @@ const scope3 = {
521545
const component3 = new Tpl(null, scope3) // in this case the `namespaceURI` is not ignored, element is rendered under `http://some.other.ns/myns`
522546
```
523547

524-
## JSX
548+
## Initialization API
525549

526-
ef.js now comes with JSX support since v0.9.0. Demo [here](https://codepan.net/gist/192a1870d23e05d775d3667389162e63).
550+
Initialization APIs are added since v0.17.0.
527551

528-
### JSX Fragments
552+
**NOTE:** `state` and `$data` are not fully initialized yet upon initlization. They're passed here only for reference. You should retrive any mount point or reactive value within your handler methods.
529553

530-
ef.js supports JSX fragments. You can create fragments just like what you do in React:
531-
```jsx
532-
<>
533-
<h1>Hello JSX!</h1>
534-
<MyCustomComponent>Now ef.js comes with JSX fragment support!</MyCustomComponent>
535-
</>
536-
```
554+
```js
555+
import Tpl from './template.eft'
556+
import Component from './my-component.eft'
557+
558+
const App = class extends Tpl {
559+
560+
// Prepare initial methods. The return value will be used as-is.
561+
static initMethods(state, $data, watch) {
562+
let count = 0
563+
return {
564+
clickBtn() {
565+
count += 1
566+
$data.count = count
567+
alert(`You have clicked ${count} times!`)
568+
}
569+
}
570+
}
537571

538-
**Note:** JSX fragments are not always the same from ef fragments. No ef bindings can be set on JSX fragments in the meantime.
572+
// Prepare initial data. The return value is non-reactive.
573+
static initData(state, $data, watch) {
574+
// Watch is equivalent for '$subscribe', but you have to use `watch` instead during initlization.
575+
watch('count', ({value}) => {
576+
console.log('Count has changed to', value)
577+
})
539578

540-
### With Transpilers
579+
return {
580+
count: 'You have not clicked.'
581+
btnText: 'Click Me!'
582+
}
583+
}
541584

542-
**Babel:** As documented [here](https://babeljs.io/docs/en/babel-preset-react), you can customize your jsx pragma when using babel. For example:
543-
```cson
544-
{
545-
"presets": [
546-
[
547-
"@babel/preset-react",
548-
{
549-
"pragma": "ef.createElement", // default pragma is React.createElement
550-
"pragmaFrag": "ef.Fragment", // default is React.Fragment
551-
"throwIfNamespace": false // defaults to true
552-
}
553-
]
554-
]
585+
// Prepare scope. The return value will be merged and supress previous assigned values.
586+
static initScope(state, $data, watch) {
587+
return {
588+
// In this case, all `div` will be rendered as `h1`, all `MyComponent` will be rendered as `Component`
589+
div: 'h1',
590+
MyComponent: Component
591+
}
592+
}
593+
594+
// Notice: overriding `init` will supress all above methods.
595+
static init(state, $data, watch) {
596+
return {
597+
methods: {},
598+
data: {},
599+
scope: {},
600+
beforeMount() {},
601+
afterMount() {},
602+
beforeUmount() {},
603+
afterUmount() {},
604+
beforeDestroy() {},
605+
afterDestroy() {},
606+
onCreated() {}
607+
}
608+
}
555609
}
556610
```
557611

558-
**Buble:** A [pull request on custom `Fragment` pragma](https://github.com/bublejs/buble/pull/199) has been merged but not yet properly [documented](https://buble.surge.sh/guide/#using-the-javascript-api). Below is a correct example:
559-
```js
560-
var output = buble.transform( input, {
561-
...
562-
563-
// custom JSX pragma
564-
jsx: 'ef.createElement',
565-
jsxFragment: 'ef.Fragment',
612+
## JSX
566613

567-
...
568-
}
569-
```
614+
JSX support was removed since v0.17.0.
570615

571616
## Server Side Rendering
572617

config/rollup.base.mjs

Lines changed: 8 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,13 @@
11
import chalk from 'chalk'
22

33
// Rollup plugins
4-
import {eslint} from 'rollup-plugin-eslint'
5-
import {terser} from 'rollup-plugin-terser'
6-
import buble from '@rollup/plugin-buble'
7-
import replace from '@rollup/plugin-replace'
4+
import eslint from '@rollup/plugin-eslint'
85
import resolve from '@rollup/plugin-node-resolve'
96
import commonjs from '@rollup/plugin-commonjs'
7+
import esbuild from 'rollup-plugin-esbuild'
108
import progress from 'rollup-plugin-progress'
119

12-
switch (process.env.BUILD_ENV) {
13-
case 'DEV': {
14-
console.log(chalk.cyan('+--------------=+| DEVELOP BUILD |+=--------------+'))
15-
break
16-
}
17-
case 'CI': {
18-
console.log(chalk.green('+--------------=+| CI BUILD |+=--------------+'))
19-
break
20-
}
21-
default: {
22-
console.log(chalk.yellow('+--------------=+| NORMAL BUILD |+=--------------+'))
23-
}
24-
}
10+
const isProduction = process.env.NODE_ENV === 'production'
2511

2612
// Log build environment
2713
console.log('Build Target:', chalk.bold.green(process.env.BUILD_TARGET || 'development'))
@@ -47,19 +33,10 @@ export default {
4733
browser: true,
4834
}),
4935
commonjs(),
50-
replace({
51-
preventAssignment: true,
52-
values: {
53-
'process.env.NODE_ENV': `'${process.env.BUILD_TARGET || 'development'}'`
54-
}
55-
}),
56-
buble({
57-
transforms: {
58-
modules: false,
59-
dangerousForOf: true
60-
},
61-
objectAssign: 'Object.assign'
62-
}),
63-
(process.env.BUILD_TARGET === 'production' && terser())
36+
esbuild({
37+
target: 'es2015',
38+
sourceMap: true,
39+
minify: isProduction
40+
})
6441
]
6542
}

config/rollup.prod.mjs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
// Import base config
22
import base from './rollup.base.mjs'
33

4-
if (process.env.BUILD_TARGET === 'production') base.output.file = `${base.proPath}/${base.bundle}.min.js`
5-
else base.output.file = `${base.proPath}/${base.bundle}.dev.js`
6-
7-
base.output.sourcemap = process.env.BUILD_ENV === 'DEMO' || process.env.BUILD_ENV === 'CI' ? base.output.sourcemap : false
4+
base.output.file = `${base.proPath}/${base.bundle}.min.js`
85

96
delete base.bundle
107
delete base.devPath

0 commit comments

Comments
 (0)