fusee-framework 1.0.1 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/app/components/Counter.ts +0 -2
- package/app/components/CounterSection.ts +1 -2
- package/app/components/UserForm.js +0 -2
- package/app/main.js +1 -1
- package/app/routes/About.js +0 -2
- package/app/routes/Home.js +0 -1
- package/app/routes/Home.ts +3 -5
- package/app/routes/TestFor.js +0 -2
- package/app/routes/TestFor.ts +54 -0
- package/auto-imports.d.ts +19 -0
- package/framework/auto-import-preset.js +8 -0
- package/framework/bin/create-fusee.js +85 -33
- package/framework/core/compiler.js +45 -42
- package/framework/core/directives.js +292 -280
- package/framework/index.js +1 -0
- package/framework/router/router.js +5 -3
- package/framework/types/compiler.d.ts +0 -10
- package/framework/types/index.d.ts +7 -0
- package/index.html +12 -3
- package/package.json +2 -1
- package/tsconfig.json +7 -2
- package/vite.config.js +11 -3
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Fusée Framework 🚀
|
|
2
|
-
**v1.
|
|
2
|
+
**v1.1.1 — Signals-First JS Framework | Atomic Reactivity | Peak Performance**
|
|
3
3
|
|
|
4
4
|
Fusée is a custom, high-performance fine-grained reactive JavaScript framework built for speed and simplicity. It features a recursive, non-greedy compiler, a signals-based reactivity engine, and a comprehensive CLI for instant application scaffolding.
|
|
5
5
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { defineComponent, signal, computed, effect, batch, untrack, onMount, onUnmount, type Signal, type Computed } from '../../framework/index.js'
|
|
2
1
|
|
|
3
2
|
type CounterProps = {
|
|
4
3
|
initialValue?: string
|
|
@@ -96,4 +95,3 @@ export const Counter = defineComponent({
|
|
|
96
95
|
}
|
|
97
96
|
}
|
|
98
97
|
})
|
|
99
|
-
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { defineComponent, signal, type SignalAccessor } from '../../framework/index.js'
|
|
2
1
|
import { Counter } from './Counter.js'
|
|
3
2
|
|
|
4
3
|
type CounterSectionProps = {
|
|
@@ -22,7 +21,7 @@ export const CounterSection = defineComponent({
|
|
|
22
21
|
|
|
23
22
|
return {
|
|
24
23
|
showCounter,
|
|
25
|
-
get title() { return props.pageTitle },
|
|
24
|
+
get title() { return props.pageTitle || '' },
|
|
26
25
|
toggleCounter: () => showCounter(!showCounter()),
|
|
27
26
|
template: `
|
|
28
27
|
<div style="margin-top: 30px; border-top: 1px solid #333; padding-top: 20px;">
|
package/app/main.js
CHANGED
package/app/routes/About.js
CHANGED
package/app/routes/Home.js
CHANGED
package/app/routes/Home.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { defineComponent, signal, type SignalAccessor } from '../../framework/index.js'
|
|
2
1
|
import { UserForm } from '../components/UserForm.js'
|
|
3
2
|
import { CounterSection } from '../components/CounterSection.js'
|
|
4
3
|
|
|
5
4
|
type HomeResult = {
|
|
6
|
-
title:
|
|
5
|
+
title: Signal<string>
|
|
7
6
|
changeTitle: () => void
|
|
8
7
|
template: string
|
|
9
8
|
}
|
|
@@ -11,15 +10,14 @@ type HomeResult = {
|
|
|
11
10
|
export const Home = defineComponent({
|
|
12
11
|
components: { UserForm, CounterSection },
|
|
13
12
|
setup(): HomeResult {
|
|
14
|
-
const title = signal('Welcome!')
|
|
15
|
-
|
|
13
|
+
const title = signal<string>('Welcome!')
|
|
16
14
|
return {
|
|
17
15
|
title,
|
|
18
16
|
changeTitle: () => title('Changed Title!'),
|
|
19
17
|
template: `
|
|
20
18
|
<div class="page">
|
|
21
19
|
<h1>{{ title.toUpperCase() }}</h1>
|
|
22
|
-
<p>This is an SPA built with my own
|
|
20
|
+
<p>This is an SPA built with my own (TypeScript!) custom framework!</p>
|
|
23
21
|
<p>Made with passion by Sebastian Șomu</p>
|
|
24
22
|
|
|
25
23
|
<button @click="changeTitle" class="primary-btn" style="margin-bottom: 20px;">Change App Title</button>
|
package/app/routes/TestFor.js
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
type Item = {
|
|
2
|
+
id: number
|
|
3
|
+
name: string
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
type TestForResult = {
|
|
7
|
+
items: Signal<Item[]>
|
|
8
|
+
addItem: () => void
|
|
9
|
+
shuffleItems: () => void
|
|
10
|
+
template: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const TestFor = defineComponent({
|
|
14
|
+
setup(): TestForResult {
|
|
15
|
+
const items = signal<Item[]>([
|
|
16
|
+
{ id: 1, name: 'Item 1' },
|
|
17
|
+
{ id: 2, name: 'Item 2' },
|
|
18
|
+
{ id: 3, name: 'Item 3' }
|
|
19
|
+
])
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
items,
|
|
23
|
+
addItem: () => {
|
|
24
|
+
const nextIds = items().map(i => i.id)
|
|
25
|
+
const nextId = nextIds.length > 0 ? Math.max(...nextIds) + 1 : 1
|
|
26
|
+
items([...items(), { id: nextId, name: `Item ${nextId}` }])
|
|
27
|
+
},
|
|
28
|
+
shuffleItems: () => {
|
|
29
|
+
const shuffled = [...items()].sort(() => Math.random() - 0.5)
|
|
30
|
+
items(shuffled)
|
|
31
|
+
},
|
|
32
|
+
template: `
|
|
33
|
+
<div class="page">
|
|
34
|
+
<h1>Keyed f-for Test (TypeScript)</h1>
|
|
35
|
+
<p>Focus an input below, then click "Shuffle" or "Add". The focus should stay!</p>
|
|
36
|
+
|
|
37
|
+
<div style="margin-bottom: 20px; display: flex; gap: 10px;">
|
|
38
|
+
<button @click="addItem" class="primary-btn">Add Item</button>
|
|
39
|
+
<button @click="shuffleItems" class="primary-btn">Shuffle Items</button>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<div style="display: flex; flex-direction: column; gap: 10px;">
|
|
43
|
+
<div f-for="item in items" :key="item.id"
|
|
44
|
+
style="padding: 10px; border: 1px solid #333; display: flex; gap: 10px; align-items: center;">
|
|
45
|
+
<span>ID: {{ item.id }}</span>
|
|
46
|
+
<input type="text" :value="item.name" placeholder="Type here..."
|
|
47
|
+
style="background: #111; color: #fff; border: 1px solid #444; padding: 5px;" />
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
`
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
/* prettier-ignore */
|
|
3
|
+
// @ts-nocheck
|
|
4
|
+
// noinspection JSUnusedGlobalSymbols
|
|
5
|
+
// Generated by unplugin-auto-import
|
|
6
|
+
// biome-ignore lint: disable
|
|
7
|
+
export {}
|
|
8
|
+
declare global {
|
|
9
|
+
const batch: typeof import('fusee-framework').batch
|
|
10
|
+
const computed: typeof import('fusee-framework').computed
|
|
11
|
+
const defineComponent: typeof import('fusee-framework').defineComponent
|
|
12
|
+
const effect: typeof import('fusee-framework').effect
|
|
13
|
+
const mountOutlet: typeof import('fusee-framework').mountOutlet
|
|
14
|
+
const mountTemplate: typeof import('fusee-framework').mountTemplate
|
|
15
|
+
const onMount: typeof import('fusee-framework').onMount
|
|
16
|
+
const onUnmount: typeof import('fusee-framework').onUnmount
|
|
17
|
+
const signal: typeof import('fusee-framework').signal
|
|
18
|
+
const untrack: typeof import('fusee-framework').untrack
|
|
19
|
+
}
|
|
@@ -6,7 +6,16 @@ import { fileURLToPath } from 'url';
|
|
|
6
6
|
|
|
7
7
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
8
8
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
// Find the package root
|
|
11
|
+
let pkgRoot = __dirname;
|
|
12
|
+
while (pkgRoot !== path.dirname(pkgRoot) && !fs.existsSync(path.join(pkgRoot, 'package.json'))) {
|
|
13
|
+
pkgRoot = path.dirname(pkgRoot);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const sourceFramework = fs.existsSync(path.join(pkgRoot, 'framework'))
|
|
17
|
+
? path.join(pkgRoot, 'framework')
|
|
18
|
+
: pkgRoot;
|
|
10
19
|
|
|
11
20
|
const projectName = process.argv[2] || 'my-fusee-app';
|
|
12
21
|
const projectPath = path.isAbsolute(projectName) ? projectName : path.join(process.cwd(), projectName);
|
|
@@ -24,9 +33,8 @@ async function scaffold() {
|
|
|
24
33
|
function copyDir(src, dest, skipTypes = false) {
|
|
25
34
|
if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
|
|
26
35
|
fs.readdirSync(src).forEach(item => {
|
|
27
|
-
// Sărim folderul types SAU orice fișier .d.ts rătăcit
|
|
28
36
|
if (skipTypes && (item === 'types' || item.endsWith('.d.ts'))) return;
|
|
29
|
-
if (item === 'bin' || item === 'node_modules' || item === '.git') return;
|
|
37
|
+
if (item === 'bin' || item === 'node_modules' || item === '.git' || item === 'dist') return;
|
|
30
38
|
|
|
31
39
|
const s = path.join(src, item);
|
|
32
40
|
const d = path.join(dest, item);
|
|
@@ -35,81 +43,125 @@ async function scaffold() {
|
|
|
35
43
|
});
|
|
36
44
|
}
|
|
37
45
|
|
|
38
|
-
const coreFolders = ['app', 'app/components'];
|
|
46
|
+
const coreFolders = ['app', 'app/components', 'app/routes'];
|
|
39
47
|
coreFolders.forEach(f => {
|
|
40
48
|
const dir = path.join(projectPath, f);
|
|
41
49
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
42
50
|
});
|
|
43
51
|
|
|
44
|
-
console.log('📦 Injecting Fusée Core Engine
|
|
52
|
+
console.log('📦 Injecting Fusée Core Engine...');
|
|
45
53
|
copyDir(sourceFramework, path.join(projectPath, 'framework'), choice === 'js');
|
|
46
54
|
|
|
55
|
+
const ext = choice === 'ts' ? 'ts' : 'js';
|
|
56
|
+
|
|
57
|
+
// 1. package.json
|
|
47
58
|
const packageJson = {
|
|
48
59
|
name: projectName === '.' ? 'fusee-project' : projectName,
|
|
49
60
|
version: '1.0.0',
|
|
50
61
|
type: 'module',
|
|
51
62
|
scripts: { "dev": "vite", "build": "vite build" },
|
|
52
|
-
devDependencies: {
|
|
63
|
+
devDependencies: {
|
|
64
|
+
"vite": "^5.0.0",
|
|
65
|
+
"unplugin-auto-import": "^0.17.0"
|
|
66
|
+
}
|
|
53
67
|
};
|
|
54
68
|
if (choice === 'ts') packageJson.devDependencies["typescript"] = "^5.0.0";
|
|
55
69
|
fs.writeFileSync(path.join(projectPath, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
56
70
|
|
|
71
|
+
// 2. vite.config.js
|
|
72
|
+
const viteConfig = `import { defineConfig } from 'vite';
|
|
73
|
+
import path from 'path';
|
|
74
|
+
import AutoImport from 'unplugin-auto-import/vite';
|
|
75
|
+
import { FuseePreset } from './framework/auto-import-preset.js';
|
|
76
|
+
|
|
77
|
+
export default defineConfig({
|
|
78
|
+
resolve: {
|
|
79
|
+
alias: {
|
|
80
|
+
'fusee-framework': path.resolve(__dirname, './framework')
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
plugins: [
|
|
84
|
+
AutoImport({
|
|
85
|
+
imports: [FuseePreset],
|
|
86
|
+
dts: true // Generates auto-imports.d.ts
|
|
87
|
+
})
|
|
88
|
+
]
|
|
89
|
+
});`;
|
|
90
|
+
fs.writeFileSync(path.join(projectPath, 'vite.config.js'), viteConfig);
|
|
91
|
+
|
|
92
|
+
// 3. tsconfig.json (if TS)
|
|
57
93
|
if (choice === 'ts') {
|
|
58
|
-
const tsConfig = {
|
|
94
|
+
const tsConfig = {
|
|
95
|
+
compilerOptions: {
|
|
96
|
+
"target": "ESNext",
|
|
97
|
+
"module": "ESNext",
|
|
98
|
+
"moduleResolution": "bundler",
|
|
99
|
+
"strict": true,
|
|
100
|
+
"esModuleInterop": true,
|
|
101
|
+
"skipLibCheck": true,
|
|
102
|
+
"paths": {
|
|
103
|
+
"fusee-framework": ["./framework/types/index.d.ts"]
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"include": ["app/**/*", "framework/**/*", "auto-imports.d.ts"]
|
|
107
|
+
};
|
|
59
108
|
fs.writeFileSync(path.join(projectPath, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2));
|
|
60
109
|
}
|
|
61
110
|
|
|
62
|
-
|
|
63
|
-
const indexHtml =
|
|
64
|
-
<!DOCTYPE html>
|
|
111
|
+
// 4. index.html
|
|
112
|
+
const indexHtml = `<!DOCTYPE html>
|
|
65
113
|
<html>
|
|
66
114
|
<head>
|
|
67
115
|
<meta charset="UTF-8">
|
|
68
|
-
<title>
|
|
116
|
+
<title>Fusée App</title>
|
|
69
117
|
<style>
|
|
70
|
-
body { background: #050505; color: white; font-family: system-ui;
|
|
71
|
-
#app {
|
|
72
|
-
|
|
118
|
+
body { background: #050505; color: white; font-family: system-ui; margin: 0; }
|
|
119
|
+
#app { min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; }
|
|
120
|
+
.hero { text-align: center; margin-bottom: 2rem; }
|
|
121
|
+
h1 { color: #ff3333; font-size: 3rem; margin: 0; }
|
|
73
122
|
</style>
|
|
74
123
|
</head>
|
|
75
124
|
<body>
|
|
76
125
|
<div id="app">
|
|
77
|
-
<
|
|
78
|
-
|
|
126
|
+
<div class="hero">
|
|
127
|
+
<h1>Fusée 🚀</h1>
|
|
128
|
+
<p style="opacity: 0.6;">Modern Reactive Framework</p>
|
|
129
|
+
</div>
|
|
79
130
|
{{ Counter }}
|
|
80
131
|
</div>
|
|
81
132
|
<script type="module" src="/app/main.${ext}"></script>
|
|
82
133
|
</body>
|
|
83
|
-
</html
|
|
84
|
-
`;
|
|
134
|
+
</html>`;
|
|
85
135
|
fs.writeFileSync(path.join(projectPath, 'index.html'), indexHtml);
|
|
86
136
|
|
|
87
|
-
|
|
137
|
+
// 5. Counter component
|
|
138
|
+
const btnStyle = "background:#ff3333; color:white; border:none; width:50px; height:50px; border-radius:15px; cursor:pointer; font-size:1.5rem; transition: 0.2s;";
|
|
88
139
|
const counterContent = choice === 'ts'
|
|
89
|
-
? `
|
|
90
|
-
|
|
140
|
+
? `interface CounterResult {
|
|
141
|
+
count: Signal<number>
|
|
142
|
+
inc: () => void
|
|
143
|
+
dec: () => void
|
|
144
|
+
template: string
|
|
145
|
+
}
|
|
91
146
|
|
|
92
147
|
export const Counter = defineComponent({
|
|
93
|
-
setup() {
|
|
148
|
+
setup(): CounterResult {
|
|
94
149
|
const count = signal<number>(0);
|
|
95
150
|
return {
|
|
96
151
|
count,
|
|
97
152
|
inc: () => count(count() + 1),
|
|
98
153
|
dec: () => count(count() - 1),
|
|
99
154
|
template: \`
|
|
100
|
-
<div style="display:flex; align-items:center;
|
|
155
|
+
<div style="display:flex; align-items:center; gap:2rem; background:#111; padding:2rem; border-radius:20px; border:1px solid #222;">
|
|
101
156
|
<button @click="dec" style="${btnStyle}">-</button>
|
|
102
|
-
<span style="font-size:
|
|
157
|
+
<span style="font-size:3rem; font-weight:bold; min-width:60px; text-align:center;">{{ count }}</span>
|
|
103
158
|
<button @click="inc" style="${btnStyle}">+</button>
|
|
104
159
|
</div>
|
|
105
160
|
\`
|
|
106
161
|
};
|
|
107
162
|
}
|
|
108
163
|
});`
|
|
109
|
-
: `
|
|
110
|
-
import { defineComponent } from '../../framework/core/component.js';
|
|
111
|
-
|
|
112
|
-
export const Counter = defineComponent({
|
|
164
|
+
: `export const Counter = defineComponent({
|
|
113
165
|
setup() {
|
|
114
166
|
const count = signal(0);
|
|
115
167
|
return {
|
|
@@ -117,23 +169,23 @@ export const Counter = defineComponent({
|
|
|
117
169
|
inc: () => count(count() + 1),
|
|
118
170
|
dec: () => count(count() - 1),
|
|
119
171
|
template: \`
|
|
120
|
-
<div style="display:flex; align-items:center;
|
|
172
|
+
<div style="display:flex; align-items:center; gap:2rem; background:#111; padding:2rem; border-radius:20px; border:1px solid #222;">
|
|
121
173
|
<button @click="dec" style="${btnStyle}">-</button>
|
|
122
|
-
<span style="font-size:
|
|
174
|
+
<span style="font-size:3rem; font-weight:bold; min-width:60px; text-align:center;">{{ count }}</span>
|
|
123
175
|
<button @click="inc" style="${btnStyle}">+</button>
|
|
124
176
|
</div>
|
|
125
177
|
\`
|
|
126
178
|
};
|
|
127
179
|
}
|
|
128
180
|
});`;
|
|
129
|
-
|
|
130
181
|
fs.writeFileSync(path.join(projectPath, `app/components/Counter.${ext}`), counterContent);
|
|
131
182
|
|
|
132
|
-
|
|
133
|
-
import { Counter } from './components/Counter
|
|
183
|
+
// 6. main.js
|
|
184
|
+
const mainContent = `import { Counter } from './components/Counter';
|
|
134
185
|
|
|
135
186
|
const root = document.getElementById('app');
|
|
136
187
|
if (root) {
|
|
188
|
+
// mountTemplate is now auto-imported via Fusée!
|
|
137
189
|
mountTemplate(root.innerHTML, root, {}, { Counter });
|
|
138
190
|
}
|
|
139
191
|
`;
|
|
@@ -64,7 +64,7 @@ export function compileNode(node, context, components, effects) {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
if (node.nodeType === 1) {
|
|
67
|
-
processDirectives(node, context, components, effects)
|
|
67
|
+
if (processDirectives(node, context, components, effects)) return
|
|
68
68
|
processBindingAttrs(node, context, effects)
|
|
69
69
|
bindComponents(node, components, context, effects)
|
|
70
70
|
} else if (node.nodeType === 3) {
|
|
@@ -80,7 +80,7 @@ export function compileNode(node, context, components, effects) {
|
|
|
80
80
|
|
|
81
81
|
function compileOnce(node, context, components, effects) {
|
|
82
82
|
if (node.nodeType === 1) {
|
|
83
|
-
processDirectives(node, context, components, effects)
|
|
83
|
+
if (processDirectives(node, context, components, effects)) return
|
|
84
84
|
processBindingAttrs(node, context, effects)
|
|
85
85
|
bindComponents(node, components, context, effects)
|
|
86
86
|
} else if (node.nodeType === 3) {
|
|
@@ -167,48 +167,51 @@ function processBindingAttrs(el, context, effects) {
|
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
function bindComponents(
|
|
171
|
-
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
170
|
+
function bindComponents(el, components, context, effects) {
|
|
171
|
+
if (el.nodeType !== 1) return
|
|
172
|
+
const name = el.getAttribute('data-component')
|
|
173
|
+
if (!name) return
|
|
174
|
+
|
|
175
|
+
// Remove the attribute immediately to prevent recursive compilers from
|
|
176
|
+
// re-identifying this element as a component placeholder.
|
|
177
|
+
el.removeAttribute('data-component')
|
|
178
|
+
|
|
179
|
+
const ComponentFn = components[name]
|
|
180
|
+
if (ComponentFn) {
|
|
181
|
+
const props = {}
|
|
182
|
+
for (const attr of [...el.attributes]) {
|
|
183
|
+
const attrName = attr.name.toLowerCase()
|
|
184
|
+
|
|
185
|
+
if (attrName.startsWith('data-prop-')) {
|
|
186
|
+
props[attrName.slice(10)] = attr.value
|
|
187
|
+
} else if (attrName.startsWith('data-bind-prop-')) {
|
|
188
|
+
const propName = attrName.slice(15)
|
|
189
|
+
const expression = attr.value.trim()
|
|
190
|
+
|
|
191
|
+
if (expression in context) {
|
|
192
|
+
Object.defineProperty(props, propName, {
|
|
193
|
+
get() {
|
|
194
|
+
const val = context[expression]
|
|
195
|
+
return typeof val === 'function' && val.isSignal ? val() : val
|
|
196
|
+
},
|
|
197
|
+
enumerable: true,
|
|
198
|
+
configurable: true
|
|
199
|
+
})
|
|
200
|
+
} else {
|
|
201
|
+
Object.defineProperty(props, propName, {
|
|
202
|
+
get() {
|
|
203
|
+
return evaluateExpression(expression, context)
|
|
204
|
+
},
|
|
205
|
+
enumerable: true,
|
|
206
|
+
configurable: true
|
|
207
|
+
})
|
|
205
208
|
}
|
|
206
209
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
}
|
|
211
|
+
const childComponent = ComponentFn(props)
|
|
212
|
+
childComponent.render(el)
|
|
213
|
+
if (effects) {
|
|
214
|
+
effects.push(() => childComponent.unmount())
|
|
212
215
|
}
|
|
213
216
|
}
|
|
214
217
|
}
|