fusee-framework 1.7.0 → 1.9.0
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 +206 -139
- package/app/benchmark/benchmark.js +2 -2
- package/app/benchmark/vite.config.js +23 -0
- package/app/main.js +24 -3
- package/app/main.ts +7 -1
- package/app/pages/_layout.js +27 -0
- package/app/pages/async-work.js +65 -0
- package/app/pages/index.js +25 -5
- package/app/pages/index.ts +9 -0
- package/app/pages/router-example.js +24 -1
- package/app/pages/scroll-demo.ts +102 -0
- package/app/providers/ChildComponent.js +8 -2
- package/app/providers/ChildComponent.ts +47 -47
- package/app/providers/GrandchildComponent.js +19 -0
- package/app/providers/ParentComponent.js +5 -1
- package/app/routes/RouterExample.js +23 -1
- package/app/routes/TestResource.js +47 -0
- package/app/services.js +15 -0
- package/auto-imports.d.ts +12 -0
- package/framework/auto-import-preset.js +15 -9
- package/framework/core/async-context.js +27 -0
- package/framework/core/compiler/ast.js +167 -0
- package/framework/core/compiler/errors-list.js +47 -0
- package/framework/core/compiler/errors.js +107 -0
- package/framework/core/compiler/generator.js +363 -0
- package/framework/core/compiler/lexer.js +347 -0
- package/framework/core/compiler/main-compiler.js +115 -0
- package/framework/core/compiler/parser.js +414 -0
- package/framework/core/compiler/plugins/actions-plugin.js +263 -0
- package/framework/core/compiler/plugins/compiler-plugin.d.ts +5 -0
- package/framework/core/compiler/plugins/compiler-plugin.js +45 -0
- package/framework/core/compiler/plugins/file-router-plugin.js +301 -0
- package/framework/core/compiler/plugins/router-plugin.js +196 -0
- package/framework/core/compiler/transformer.js +398 -0
- package/framework/core/component.js +61 -23
- package/framework/core/di.js +187 -0
- package/framework/core/directives.js +40 -96
- package/framework/core/evaluator.js +1 -1
- package/framework/core/h.js +430 -0
- package/framework/core/hydration.js +83 -0
- package/framework/core/reconcile.js +151 -0
- package/framework/core/signal.js +269 -0
- package/framework/core/store.js +322 -81
- package/framework/index.js +5 -3
- package/framework/router/file-router.js +1 -1
- package/framework/router/router.js +113 -21
- package/framework/server/actions.js +173 -0
- package/framework/server/actions.server.js +88 -0
- package/framework/server/index.js +18 -0
- package/framework/types/actions.d.ts +50 -0
- package/framework/types/actions.server.d.ts +36 -0
- package/framework/types/compiler/ast.d.ts +166 -0
- package/framework/types/compiler/errors-codes.d.ts +23 -0
- package/framework/types/compiler/errors.d.ts +43 -0
- package/framework/types/compiler/generator.d.ts +8 -0
- package/framework/types/compiler/h.d.ts +46 -0
- package/framework/types/compiler/lexer.d.ts +27 -0
- package/framework/types/compiler/main-compiler.d.ts +60 -0
- package/framework/types/compiler/parser.d.ts +4 -0
- package/framework/types/compiler/plugins/actions.d.ts +30 -0
- package/framework/types/compiler/plugins/compiler-plugin.d.ts +5 -0
- package/framework/types/compiler/plugins/file-router-plugins.d.ts +32 -0
- package/framework/types/compiler/plugins/router-plugin.d.ts +24 -0
- package/framework/types/compiler/transformer.d.ts +28 -0
- package/framework/types/component.d.ts +89 -88
- package/framework/types/di.d.ts +43 -0
- package/framework/types/hydration.d.ts +30 -0
- package/framework/types/index.d.ts +38 -22
- package/framework/types/reconcile.d.ts +7 -0
- package/framework/types/router.d.ts +11 -1
- package/framework/types/server.d.ts +11 -0
- package/framework/types/signal.d.ts +142 -79
- package/framework/types/store.d.ts +66 -8
- package/ghid_go_fusee_integrat_go1_26.txt +276 -0
- package/ghid_rust_fusee_rust_1_95_0.txt +1118 -0
- package/package.json +44 -36
- package/vite.config.js +65 -47
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import {NodeType, createExpression, isDirective, findDirective, isStaticText, cloneNode,} from './ast.js'
|
|
2
|
+
import { CompileWarning } from './errors.js'
|
|
3
|
+
import { ErrorCode } from './errors-list.js'
|
|
4
|
+
|
|
5
|
+
export function transform(ast, options = {}) {
|
|
6
|
+
const ctx = {
|
|
7
|
+
components: options.components ?? new Set(),
|
|
8
|
+
source: options.source ?? '',
|
|
9
|
+
hoisted: [],
|
|
10
|
+
warnings: [],
|
|
11
|
+
scope: new Set(options.scope ?? []),
|
|
12
|
+
scopeStack: [],
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
markStaticPass(ast, ctx)
|
|
16
|
+
walkChildren(ast, node => {
|
|
17
|
+
if (node.children) chainConditionalsInChildren(node.children, ctx)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
walkChildren(ast, node => {
|
|
21
|
+
validateFor(node, ctx)
|
|
22
|
+
validateModel(node, ctx)
|
|
23
|
+
validateComponents(node, ctx)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
if (ctx.scope.size > 0) {
|
|
27
|
+
analyseScope(ast, ctx, new Set(ctx.scope))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
ast._hoisted = ctx.hoisted
|
|
31
|
+
|
|
32
|
+
return { ast, warnings: ctx.warnings }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Step 1 & 2: static marking + hoisting
|
|
36
|
+
|
|
37
|
+
function markStaticPass(node, ctx) {
|
|
38
|
+
if (!node) return false
|
|
39
|
+
|
|
40
|
+
if (node.type === NodeType.TEXT) {
|
|
41
|
+
node.isStatic = true
|
|
42
|
+
return true
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (node.type === NodeType.EXPRESSION) {
|
|
46
|
+
node.isStatic = isLiteralExpression(node.content)
|
|
47
|
+
return node.isStatic
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (node.type === NodeType.INTERPOLATION) {
|
|
51
|
+
node.isStatic = markStaticPass(node.expression, ctx)
|
|
52
|
+
return node.isStatic
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (node.type === NodeType.ELEMENT) {
|
|
56
|
+
const hasOnce = node.props.some(p => isDirective(p, 'once'))
|
|
57
|
+
if (hasOnce) {
|
|
58
|
+
node.isStatic = true
|
|
59
|
+
node.hoisted = true
|
|
60
|
+
ctx.hoisted.push(node)
|
|
61
|
+
return true
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const hasDynamic = node.props.some(p => p.type === NodeType.DIRECTIVE || p.type === NodeType.BINDING || p.type === NodeType.EVENT)
|
|
65
|
+
|
|
66
|
+
if (hasDynamic) {
|
|
67
|
+
node.isStatic = false
|
|
68
|
+
if (node.children) node.children.forEach(c => markStaticPass(c, ctx))
|
|
69
|
+
return false
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const hasInterpolatedAttr = node.props.some(p => p.type === NodeType.ATTRIBUTE && typeof p.value === 'string' && p.value.includes('{{'))
|
|
73
|
+
if (hasInterpolatedAttr) {
|
|
74
|
+
node.isStatic = false
|
|
75
|
+
if (node.children) node.children.forEach(c => markStaticPass(c, ctx))
|
|
76
|
+
return false
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const childrenStatic = node.children ? node.children.every(c => markStaticPass(c, ctx)) : true
|
|
80
|
+
|
|
81
|
+
node.isStatic = childrenStatic
|
|
82
|
+
|
|
83
|
+
if (node.isStatic && node.children && node.children.length >= 2) {
|
|
84
|
+
node.hoisted = true
|
|
85
|
+
ctx.hoisted.push(node)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return node.isStatic
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (node.type === NodeType.COMPONENT) {
|
|
92
|
+
node.isStatic = false
|
|
93
|
+
if (node.slots) {
|
|
94
|
+
for (const slotChildren of Object.values(node.slots)) {
|
|
95
|
+
if (Array.isArray(slotChildren))
|
|
96
|
+
slotChildren.forEach(c => markStaticPass(c, ctx))
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return false
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (node.type === NodeType.ROOT) {
|
|
103
|
+
if (node.children) node.children.forEach(c => markStaticPass(c, ctx))
|
|
104
|
+
return false
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return false
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Step 3: chain f-if / f-else-if / f-else
|
|
111
|
+
|
|
112
|
+
function chainConditionalsInChildren(children, ctx) {
|
|
113
|
+
for (let i = 0; i < children.length; i++) {
|
|
114
|
+
const node = children[i]
|
|
115
|
+
if (!isElementOrComponent(node)) continue
|
|
116
|
+
|
|
117
|
+
const ifDir = findDirective(node, 'if')
|
|
118
|
+
if (!ifDir) {
|
|
119
|
+
const elseDir = findDirective(node, 'else') ?? findDirective(node, 'else-if')
|
|
120
|
+
if (elseDir) {
|
|
121
|
+
ctx.warnings.push(new CompileWarning(ErrorCode.V_ELSE_NO_IF, elseDir.loc, ctx.source))
|
|
122
|
+
}
|
|
123
|
+
continue
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const branches = [{ condition: ifDir.expression, node }]
|
|
127
|
+
let j = i + 1
|
|
128
|
+
|
|
129
|
+
while (j < children.length) {
|
|
130
|
+
const next = children[j]
|
|
131
|
+
if (!isElementOrComponent(next)) {
|
|
132
|
+
j++
|
|
133
|
+
continue
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const elseIfDir = findDirective(next, 'else-if')
|
|
137
|
+
const elseDir = findDirective(next, 'else')
|
|
138
|
+
|
|
139
|
+
if (elseIfDir) {
|
|
140
|
+
branches.push({ condition: elseIfDir.expression, node: next })
|
|
141
|
+
j++
|
|
142
|
+
} else if (elseDir) {
|
|
143
|
+
branches.push({ condition: null, node: next })
|
|
144
|
+
j++
|
|
145
|
+
break
|
|
146
|
+
} else {
|
|
147
|
+
break
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const ifNode = {
|
|
152
|
+
type: 'If',
|
|
153
|
+
branches,
|
|
154
|
+
loc: node.loc,
|
|
155
|
+
isStatic: false,
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
children.splice(i, j - i, ifNode)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Step 4: f-for :key validation
|
|
163
|
+
|
|
164
|
+
function validateFor(node, ctx) {
|
|
165
|
+
if (node.type !== NodeType.ELEMENT && node.type !== NodeType.COMPONENT) return
|
|
166
|
+
|
|
167
|
+
const forDir = findDirective(node, 'for')
|
|
168
|
+
if (!forDir) return
|
|
169
|
+
|
|
170
|
+
const hasKey = node.props.some(p => p.type === NodeType.BINDING && p.name === 'key')
|
|
171
|
+
if (!hasKey) {
|
|
172
|
+
ctx.warnings.push(new CompileWarning(ErrorCode.FOR_MISSING_KEY, forDir.loc, ctx.source, node.tag ?? node.name))
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Step 5: f-model element validation
|
|
177
|
+
|
|
178
|
+
const MODEL_ALLOWED = new Set(['input', 'textarea', 'select'])
|
|
179
|
+
|
|
180
|
+
function validateModel(node, ctx) {
|
|
181
|
+
if (node.type !== NodeType.ELEMENT) return
|
|
182
|
+
const modelDir = findDirective(node, 'model')
|
|
183
|
+
if (!modelDir) return
|
|
184
|
+
if (!MODEL_ALLOWED.has(node.tag.toLowerCase())) {
|
|
185
|
+
ctx.warnings.push(new CompileWarning(ErrorCode.MODEL_ON_NON_INPUT, modelDir.loc, ctx.source, node.tag))
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Step 6: component registration validation
|
|
190
|
+
|
|
191
|
+
function validateComponents(node, ctx) {
|
|
192
|
+
if (node.type !== NodeType.COMPONENT) return
|
|
193
|
+
if (!ctx.components.has(node.name)) {
|
|
194
|
+
ctx.warnings.push(new CompileWarning(ErrorCode.COMPONENT_NOT_REGISTERED, node.loc, ctx.source, node.name))
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ─── Step 7 & 8: Semantic analysis — scope + expression validation ────────────
|
|
199
|
+
|
|
200
|
+
function analyseScope(node, ctx, localScope) {
|
|
201
|
+
if (!node) return
|
|
202
|
+
|
|
203
|
+
switch (node.type) {
|
|
204
|
+
case NodeType.ROOT:
|
|
205
|
+
for (const child of node.children ?? []) {
|
|
206
|
+
analyseScope(child, ctx, localScope)
|
|
207
|
+
}
|
|
208
|
+
break
|
|
209
|
+
|
|
210
|
+
case NodeType.ELEMENT:
|
|
211
|
+
case NodeType.COMPONENT: {
|
|
212
|
+
const forDir = findDirective(node, 'for')
|
|
213
|
+
const childScope = forDir ? _extendScopeFromFor(localScope, forDir.arg) : localScope
|
|
214
|
+
|
|
215
|
+
for (const prop of node.props ?? []) {
|
|
216
|
+
_validateProp(prop, ctx, childScope)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
for (const child of node.children ?? []) {
|
|
220
|
+
analyseScope(child, ctx, childScope)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (node.slots) {
|
|
224
|
+
for (const slotChildren of Object.values(node.slots)) {
|
|
225
|
+
if (Array.isArray(slotChildren)) {
|
|
226
|
+
for (const c of slotChildren)
|
|
227
|
+
analyseScope(c, ctx, childScope)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
break
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
case NodeType.INTERPOLATION:
|
|
235
|
+
_validateExpression(node.expression, ctx, localScope)
|
|
236
|
+
break
|
|
237
|
+
|
|
238
|
+
case 'If':
|
|
239
|
+
for (const branch of node.branches ?? []) {
|
|
240
|
+
if (branch.condition) _validateExpression(branch.condition, ctx, localScope)
|
|
241
|
+
analyseScope(branch.node, ctx, localScope)
|
|
242
|
+
}
|
|
243
|
+
break
|
|
244
|
+
|
|
245
|
+
case NodeType.TEXT:
|
|
246
|
+
case NodeType.EXPRESSION:
|
|
247
|
+
case NodeType.SLOT_OUTLET:
|
|
248
|
+
case NodeType.SLOT_CONTENT:
|
|
249
|
+
break
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function _validateProp(prop, ctx, scope) {
|
|
254
|
+
switch (prop.type) {
|
|
255
|
+
case NodeType.BINDING:
|
|
256
|
+
_validateExpression(prop.expression, ctx, scope)
|
|
257
|
+
break
|
|
258
|
+
|
|
259
|
+
case NodeType.EVENT:
|
|
260
|
+
_validateExpression(prop.expression, ctx, scope, { isHandler: true })
|
|
261
|
+
break
|
|
262
|
+
|
|
263
|
+
case NodeType.DIRECTIVE:
|
|
264
|
+
if (prop.name === 'for')
|
|
265
|
+
break
|
|
266
|
+
if (prop.expression)
|
|
267
|
+
_validateExpression(prop.expression, ctx, scope)
|
|
268
|
+
break
|
|
269
|
+
|
|
270
|
+
case NodeType.ATTRIBUTE:
|
|
271
|
+
break
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function _validateExpression(exprNode, ctx, scope, opts = {}) {
|
|
276
|
+
if (!exprNode || !exprNode.content) return
|
|
277
|
+
if (exprNode.isStatic) return
|
|
278
|
+
|
|
279
|
+
const raw = exprNode.content.trim()
|
|
280
|
+
if (!raw) return
|
|
281
|
+
|
|
282
|
+
const identifiers = _extractIdentifiers(raw)
|
|
283
|
+
|
|
284
|
+
for (const id of identifiers) {
|
|
285
|
+
if (JS_GLOBALS.has(id)) continue
|
|
286
|
+
if (opts.isHandler && id === '$event') continue
|
|
287
|
+
|
|
288
|
+
if (!scope.has(id)) {
|
|
289
|
+
ctx.warnings.push(new CompileWarning(
|
|
290
|
+
ErrorCode.UNKNOWN_IDENTIFIER,
|
|
291
|
+
exprNode.loc,
|
|
292
|
+
ctx.source,
|
|
293
|
+
id,
|
|
294
|
+
raw
|
|
295
|
+
))
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (exprNode._isForKey && isLiteralExpression(raw)) {
|
|
300
|
+
ctx.warnings.push(new CompileWarning(
|
|
301
|
+
ErrorCode.STATIC_FOR_KEY,
|
|
302
|
+
exprNode.loc,
|
|
303
|
+
ctx.source,
|
|
304
|
+
raw
|
|
305
|
+
))
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function _extractIdentifiers(expr) {
|
|
310
|
+
const stripped = expr
|
|
311
|
+
.replace(/'[^'\\]*(?:\\.[^'\\]*)*'/g, '""')
|
|
312
|
+
.replace(/"[^"\\]*(?:\\.[^"\\]*)*"/g, '""')
|
|
313
|
+
.replace(/`[^`\\]*(?:\\.[^`\\]*)*`/g, '""')
|
|
314
|
+
|
|
315
|
+
const found = new Set()
|
|
316
|
+
const RE = /(?<![a-zA-Z0-9_$])([a-zA-Z_$][a-zA-Z0-9_$]*)(?![a-zA-Z0-9_$])/g
|
|
317
|
+
let m
|
|
318
|
+
|
|
319
|
+
while ((m = RE.exec(stripped)) !== null) {
|
|
320
|
+
const id = m[1]
|
|
321
|
+
const idx = m.index
|
|
322
|
+
|
|
323
|
+
if (JS_KEYWORDS.has(id)) continue
|
|
324
|
+
if (idx > 0 && stripped[idx - 1] === '.') continue
|
|
325
|
+
|
|
326
|
+
found.add(id)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return found
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function _extendScopeFromFor(parentScope, forArg) {
|
|
333
|
+
if (!forArg)
|
|
334
|
+
return parentScope
|
|
335
|
+
|
|
336
|
+
const extended = new Set(parentScope)
|
|
337
|
+
|
|
338
|
+
if (forArg.item)
|
|
339
|
+
extended.add(forArg.item)
|
|
340
|
+
if (forArg.index)
|
|
341
|
+
extended.add(forArg.index)
|
|
342
|
+
|
|
343
|
+
return extended
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const JS_GLOBALS = new Set([
|
|
347
|
+
'Math', 'Object', 'Array', 'String', 'Number', 'Boolean', 'Symbol',
|
|
348
|
+
'Date', 'RegExp', 'Map', 'Set', 'WeakMap', 'WeakSet', 'WeakRef',
|
|
349
|
+
'Promise', 'Proxy', 'Reflect', 'JSON', 'Error', 'TypeError',
|
|
350
|
+
'RangeError', 'parseInt', 'parseFloat', 'isNaN', 'isFinite',
|
|
351
|
+
'encodeURIComponent', 'decodeURIComponent', 'encodeURI', 'decodeURI',
|
|
352
|
+
'console', 'window', 'document', 'globalThis', 'undefined', 'null',
|
|
353
|
+
'Infinity', 'NaN', 'setTimeout', 'clearTimeout', 'setInterval',
|
|
354
|
+
'clearInterval', 'queueMicrotask', 'fetch', 'true', 'false'
|
|
355
|
+
])
|
|
356
|
+
|
|
357
|
+
const JS_KEYWORDS = new Set([
|
|
358
|
+
'if', 'else', 'for', 'while', 'do', 'switch', 'case', 'break',
|
|
359
|
+
'continue', 'return', 'throw', 'try', 'catch', 'finally',
|
|
360
|
+
'new', 'delete', 'typeof', 'instanceof', 'in', 'of', 'void',
|
|
361
|
+
'const', 'let', 'var', 'function', 'class', 'import', 'export',
|
|
362
|
+
'default', 'extends', 'super', 'this', 'yield', 'await', 'async',
|
|
363
|
+
'true', 'false', 'null', 'undefined'
|
|
364
|
+
])
|
|
365
|
+
|
|
366
|
+
function walkChildren(node, visitor) {
|
|
367
|
+
if (!node) return
|
|
368
|
+
visitor(node)
|
|
369
|
+
|
|
370
|
+
if (node.children) {
|
|
371
|
+
for (const child of node.children)
|
|
372
|
+
walkChildren(child, visitor)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (node.branches) {
|
|
376
|
+
for (const branch of node.branches)
|
|
377
|
+
walkChildren(branch.node, visitor)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (node.slots) {
|
|
381
|
+
for (const slotChildren of Object.values(node.slots)) {
|
|
382
|
+
if (Array.isArray(slotChildren)) {
|
|
383
|
+
for (const c of slotChildren)
|
|
384
|
+
walkChildren(c, visitor)
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function isElementOrComponent(node) {
|
|
391
|
+
return node?.type === NodeType.ELEMENT || node?.type === NodeType.COMPONENT
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const LITERAL_RE = /^(?:true|false|null|undefined|-?\d[\d._]*(?:n)?|'[^']*'|"[^"]*"|`[^`]*`)$/
|
|
395
|
+
|
|
396
|
+
function isLiteralExpression(expr) {
|
|
397
|
+
return LITERAL_RE.test(expr.trim())
|
|
398
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { effect, setEffectHook, batch } from './signal.js'
|
|
2
2
|
import { mountTemplate } from './compiler.js'
|
|
3
|
+
import { rootInjector, runInContext, replaceActiveInjector, EnvironmentInjector } from './di.js'
|
|
4
|
+
export { inject } from './di.js'
|
|
3
5
|
|
|
4
6
|
let currentInstance = null
|
|
5
7
|
|
|
@@ -129,20 +131,25 @@ export function onUnmount(fn) {
|
|
|
129
131
|
|
|
130
132
|
export function defineComponent(options) {
|
|
131
133
|
return function ComponentFactory(props = {}, { listeners = {}, slots = {}, parent = null } = {}) {
|
|
134
|
+
const parentInjector = (parent && parent._injector) || rootInjector;
|
|
135
|
+
|
|
132
136
|
const instance = {
|
|
133
137
|
props: options.props ? resolveProps(options.props, props) : props,
|
|
134
138
|
_mountHooks: [],
|
|
135
139
|
_unmountHooks: [],
|
|
136
140
|
_effects: [],
|
|
137
141
|
_element: null,
|
|
138
|
-
_provides: {},
|
|
139
142
|
_parent: parent,
|
|
143
|
+
_injector: parentInjector,
|
|
144
|
+
_ownsInjector: false
|
|
140
145
|
}
|
|
141
146
|
|
|
142
147
|
const emit = createEmit(listeners)
|
|
143
148
|
|
|
144
149
|
currentInstance = instance
|
|
145
|
-
const result =
|
|
150
|
+
const result = runInContext(instance._injector, () => {
|
|
151
|
+
return options.setup(instance.props, { emit, slots });
|
|
152
|
+
});
|
|
146
153
|
instance.state = result
|
|
147
154
|
result._instance = instance
|
|
148
155
|
currentInstance = null
|
|
@@ -150,17 +157,41 @@ export function defineComponent(options) {
|
|
|
150
157
|
function render(container) {
|
|
151
158
|
instance._element = container
|
|
152
159
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
160
|
+
if (options.render) {
|
|
161
|
+
const nodes = options.render(result, options.components || {})
|
|
162
|
+
container.innerHTML = ''
|
|
163
|
+
|
|
164
|
+
function mountNode(fnode) {
|
|
165
|
+
if (!fnode || !fnode.node) return
|
|
166
|
+
container.appendChild(fnode.node)
|
|
167
|
+
|
|
168
|
+
if (fnode.effects) {
|
|
169
|
+
instance._effects.push(...fnode.effects)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (fnode.children) {
|
|
173
|
+
for (const child of fnode.children) {
|
|
174
|
+
mountNode(child)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
for (const node of nodes) {
|
|
180
|
+
mountNode(node)
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
const resolvedTemplate = resolveSlots(result.template, slots)
|
|
184
|
+
const { effects } = mountTemplate(
|
|
185
|
+
resolvedTemplate,
|
|
186
|
+
container,
|
|
187
|
+
result,
|
|
188
|
+
options.components || {}
|
|
189
|
+
)
|
|
190
|
+
instance._effects.push(...effects)
|
|
191
|
+
}
|
|
161
192
|
|
|
162
|
-
instance.
|
|
163
|
-
|
|
193
|
+
for (const hook of instance._mountHooks)
|
|
194
|
+
hook()
|
|
164
195
|
|
|
165
196
|
return instance
|
|
166
197
|
}
|
|
@@ -183,17 +214,21 @@ export function defineComponent(options) {
|
|
|
183
214
|
}
|
|
184
215
|
|
|
185
216
|
export function provide(key, value) {
|
|
186
|
-
if (!currentInstance) return
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
217
|
+
if (!currentInstance) return;
|
|
218
|
+
|
|
219
|
+
if (!currentInstance._ownsInjector) {
|
|
220
|
+
currentInstance._injector = new EnvironmentInjector([], currentInstance._injector);
|
|
221
|
+
currentInstance._ownsInjector = true;
|
|
222
|
+
replaceActiveInjector(currentInstance._injector);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (value === undefined && key && key.provide) {
|
|
226
|
+
currentInstance._injector.provide(key);
|
|
227
|
+
} else if (value && typeof value === 'object' && (value.useClass || value.useFactory || value.useExisting)) {
|
|
228
|
+
currentInstance._injector.provide({ provide: key, ...value });
|
|
229
|
+
} else {
|
|
230
|
+
currentInstance._injector.provide({ provide: key, useValue: value });
|
|
195
231
|
}
|
|
196
|
-
return null;
|
|
197
232
|
}
|
|
198
233
|
|
|
199
234
|
export function defineAsyncComponent(loaderOrOptions) {
|
|
@@ -202,14 +237,17 @@ export function defineAsyncComponent(loaderOrOptions) {
|
|
|
202
237
|
: loaderOrOptions
|
|
203
238
|
|
|
204
239
|
return function AsyncComponentFactory(props = {}, { listeners = {}, slots = {}, parent = null } = {}) {
|
|
240
|
+
const parentInjector = (parent && parent._injector) || rootInjector;
|
|
241
|
+
|
|
205
242
|
const instance = {
|
|
206
243
|
props,
|
|
207
244
|
_mountHooks: [],
|
|
208
245
|
_unmountHooks: [],
|
|
209
246
|
_effects: [],
|
|
210
247
|
_element: null,
|
|
211
|
-
_provides: {},
|
|
212
248
|
_parent: parent,
|
|
249
|
+
_injector: parentInjector,
|
|
250
|
+
_ownsInjector: false
|
|
213
251
|
}
|
|
214
252
|
|
|
215
253
|
let childApi = null
|