@fluffjs/cli 0.0.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/Cli.d.ts +37 -0
- package/Cli.js +596 -0
- package/CodeGenerator.d.ts +42 -0
- package/CodeGenerator.js +767 -0
- package/ComponentCompiler.d.ts +28 -0
- package/ComponentCompiler.js +354 -0
- package/ControlFlowParser.d.ts +55 -0
- package/ControlFlowParser.js +279 -0
- package/ExpressionTransformer.d.ts +30 -0
- package/ExpressionTransformer.js +276 -0
- package/Generator.d.ts +21 -0
- package/Generator.js +338 -0
- package/IndexHtmlTransformer.d.ts +9 -0
- package/IndexHtmlTransformer.js +97 -0
- package/TemplateParser.d.ts +27 -0
- package/TemplateParser.js +330 -0
- package/babel-plugin-class-transform.d.ts +20 -0
- package/babel-plugin-class-transform.js +40 -0
- package/babel-plugin-component.d.ts +14 -0
- package/babel-plugin-component.js +76 -0
- package/babel-plugin-imports.d.ts +13 -0
- package/babel-plugin-imports.js +76 -0
- package/babel-plugin-reactive.d.ts +22 -0
- package/babel-plugin-reactive.js +411 -0
- package/bin.d.ts +3 -0
- package/bin.js +10 -0
- package/fluff-esbuild-plugin.d.ts +8 -0
- package/fluff-esbuild-plugin.js +59 -0
- package/index.d.ts +7 -0
- package/index.js +4 -0
- package/package.json +39 -0
- package/types.d.ts +46 -0
- package/types.js +1 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
import { types as t } from '@babel/core';
|
|
2
|
+
export const reactivePropertiesMap = new Map();
|
|
3
|
+
export default function reactivePlugin() {
|
|
4
|
+
return {
|
|
5
|
+
name: 'babel-plugin-reactive', visitor: {
|
|
6
|
+
Program: {
|
|
7
|
+
enter(path, state) {
|
|
8
|
+
state.needsPropertyImport = false;
|
|
9
|
+
state.reactiveProperties = new Set();
|
|
10
|
+
state.watchMethods = [];
|
|
11
|
+
}, exit(path, state) {
|
|
12
|
+
const filename = state.filename ?? 'unknown';
|
|
13
|
+
if (state.reactiveProperties && state.reactiveProperties.size > 0) {
|
|
14
|
+
reactivePropertiesMap.set(filename, state.reactiveProperties);
|
|
15
|
+
}
|
|
16
|
+
if (state.needsPropertyImport) {
|
|
17
|
+
const hasPropertyImport = path.node.body.some(node => {
|
|
18
|
+
if (t.isImportDeclaration(node)) {
|
|
19
|
+
return node.specifiers.some(spec => t.isImportSpecifier(spec) && t.isIdentifier(spec.imported) && spec.imported.name === 'Property');
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
});
|
|
23
|
+
if (!hasPropertyImport) {
|
|
24
|
+
const importDecl = t.importDeclaration([t.importSpecifier(t.identifier('Property'), t.identifier('Property'))], t.stringLiteral('@fluffjs/fluff'));
|
|
25
|
+
path.node.body.unshift(importDecl);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
ClassBody(path, state) {
|
|
31
|
+
const watchCalls = [];
|
|
32
|
+
const watchCallProps = [];
|
|
33
|
+
for (const memberPath of path.get('body')) {
|
|
34
|
+
if (!memberPath.isClassProperty())
|
|
35
|
+
continue;
|
|
36
|
+
const init = memberPath.node.value;
|
|
37
|
+
if (!init || !t.isCallExpression(init))
|
|
38
|
+
continue;
|
|
39
|
+
const { callee } = init;
|
|
40
|
+
if (!t.isMemberExpression(callee))
|
|
41
|
+
continue;
|
|
42
|
+
if (!t.isThisExpression(callee.object))
|
|
43
|
+
continue;
|
|
44
|
+
if (!t.isIdentifier(callee.property) || callee.property.name !== '$watch')
|
|
45
|
+
continue;
|
|
46
|
+
const propName = t.isIdentifier(memberPath.node.key) ? memberPath.node.key.name : null;
|
|
47
|
+
if (!propName)
|
|
48
|
+
continue;
|
|
49
|
+
const args = init.arguments;
|
|
50
|
+
if (args.length < 2)
|
|
51
|
+
continue;
|
|
52
|
+
const [propsArg, callbackArg] = args;
|
|
53
|
+
if (!t.isArrayExpression(propsArg))
|
|
54
|
+
continue;
|
|
55
|
+
const watchedProps = propsArg.elements
|
|
56
|
+
.filter((el) => t.isStringLiteral(el))
|
|
57
|
+
.map(el => el.value);
|
|
58
|
+
watchCalls.push({ propName, watchedProps, callbackArg });
|
|
59
|
+
watchCallProps.push(memberPath);
|
|
60
|
+
}
|
|
61
|
+
for (const p of watchCallProps) {
|
|
62
|
+
p.remove();
|
|
63
|
+
}
|
|
64
|
+
const newMembers = [];
|
|
65
|
+
const propsToRemove = [];
|
|
66
|
+
const watchMethods = [];
|
|
67
|
+
const privateFields = [];
|
|
68
|
+
const getterHostBindingUpdates = [];
|
|
69
|
+
const propertyHostBindingInits = [];
|
|
70
|
+
const pipeMethods = [];
|
|
71
|
+
const hostListeners = [];
|
|
72
|
+
for (const memberPath of path.get('body')) {
|
|
73
|
+
if (memberPath.isClassMethod()) {
|
|
74
|
+
const methodNode = memberPath.node;
|
|
75
|
+
const decorators = methodNode.decorators ?? [];
|
|
76
|
+
const hostListenerIndex = decorators.findIndex(dec => {
|
|
77
|
+
if (t.isCallExpression(dec.expression) && t.isIdentifier(dec.expression.callee)) {
|
|
78
|
+
return dec.expression.callee.name === 'HostListener';
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
});
|
|
82
|
+
if (hostListenerIndex >= 0) {
|
|
83
|
+
const hostListenerDecorator = decorators[hostListenerIndex];
|
|
84
|
+
if (t.isCallExpression(hostListenerDecorator.expression)) {
|
|
85
|
+
const args = hostListenerDecorator.expression.arguments;
|
|
86
|
+
if (args.length > 0 && t.isStringLiteral(args[0])) {
|
|
87
|
+
const eventName = args[0].value;
|
|
88
|
+
if (t.isIdentifier(methodNode.key)) {
|
|
89
|
+
hostListeners.push({
|
|
90
|
+
methodName: methodNode.key.name, eventName
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
decorators.splice(hostListenerIndex, 1);
|
|
96
|
+
}
|
|
97
|
+
const pipeDecoratorIndex = decorators.findIndex(dec => {
|
|
98
|
+
if (t.isCallExpression(dec.expression) && t.isIdentifier(dec.expression.callee)) {
|
|
99
|
+
return dec.expression.callee.name === 'Pipe';
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
});
|
|
103
|
+
if (pipeDecoratorIndex >= 0) {
|
|
104
|
+
const pipeDecorator = decorators[pipeDecoratorIndex];
|
|
105
|
+
if (t.isCallExpression(pipeDecorator.expression)) {
|
|
106
|
+
const args = pipeDecorator.expression.arguments;
|
|
107
|
+
if (args.length > 0 && t.isStringLiteral(args[0])) {
|
|
108
|
+
const pipeName = args[0].value;
|
|
109
|
+
if (t.isIdentifier(methodNode.key)) {
|
|
110
|
+
pipeMethods.push({
|
|
111
|
+
pipeName, methodName: methodNode.key.name
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
decorators.splice(pipeDecoratorIndex, 1);
|
|
117
|
+
}
|
|
118
|
+
const watchDecoratorIndex = decorators.findIndex(dec => {
|
|
119
|
+
if (t.isCallExpression(dec.expression) && t.isIdentifier(dec.expression.callee)) {
|
|
120
|
+
return dec.expression.callee.name === 'Watch';
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
123
|
+
});
|
|
124
|
+
if (watchDecoratorIndex >= 0) {
|
|
125
|
+
const watchDecorator = decorators[watchDecoratorIndex];
|
|
126
|
+
if (t.isCallExpression(watchDecorator.expression)) {
|
|
127
|
+
const args = watchDecorator.expression.arguments;
|
|
128
|
+
const watchedProps = args
|
|
129
|
+
.filter((arg) => t.isStringLiteral(arg))
|
|
130
|
+
.map(arg => arg.value);
|
|
131
|
+
if (t.isIdentifier(methodNode.key) && watchedProps.length > 0) {
|
|
132
|
+
watchMethods.push({
|
|
133
|
+
methodName: methodNode.key.name, watchedProps
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
decorators.splice(watchDecoratorIndex, 1);
|
|
138
|
+
}
|
|
139
|
+
if (methodNode.kind === 'get') {
|
|
140
|
+
const hostBindingIndex = decorators.findIndex(dec => {
|
|
141
|
+
if (t.isCallExpression(dec.expression) && t.isIdentifier(dec.expression.callee)) {
|
|
142
|
+
return dec.expression.callee.name === 'HostBinding';
|
|
143
|
+
}
|
|
144
|
+
return false;
|
|
145
|
+
});
|
|
146
|
+
if (hostBindingIndex >= 0) {
|
|
147
|
+
const hostBindingDecorator = decorators[hostBindingIndex];
|
|
148
|
+
if (t.isCallExpression(hostBindingDecorator.expression)) {
|
|
149
|
+
const args = hostBindingDecorator.expression.arguments;
|
|
150
|
+
if (args.length > 0 && t.isStringLiteral(args[0]) && t.isIdentifier(methodNode.key)) {
|
|
151
|
+
const hostProperty = args[0].value;
|
|
152
|
+
const getterName = methodNode.key.name;
|
|
153
|
+
let updateStatement = t.emptyStatement();
|
|
154
|
+
if (hostProperty.startsWith('class.')) {
|
|
155
|
+
const className = hostProperty.slice(6);
|
|
156
|
+
updateStatement = t.ifStatement(t.identifier('__v'), t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier('classList')), t.identifier('add')), [t.stringLiteral(className)])), t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier('classList')), t.identifier('remove')), [t.stringLiteral(className)])));
|
|
157
|
+
}
|
|
158
|
+
else if (hostProperty.startsWith('attr.')) {
|
|
159
|
+
const attrName = hostProperty.slice(5);
|
|
160
|
+
updateStatement = t.ifStatement(t.binaryExpression('!=', t.identifier('__v'), t.nullLiteral()), t.expressionStatement(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('setAttribute')), [
|
|
161
|
+
t.stringLiteral(attrName),
|
|
162
|
+
t.callExpression(t.identifier('String'), [t.identifier('__v')])
|
|
163
|
+
])), t.expressionStatement(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('removeAttribute')), [t.stringLiteral(attrName)])));
|
|
164
|
+
}
|
|
165
|
+
else if (hostProperty.startsWith('style.')) {
|
|
166
|
+
const styleProp = hostProperty.slice(6);
|
|
167
|
+
updateStatement = t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier('style')), t.identifier(styleProp)), t.logicalExpression('||', t.identifier('__v'), t.stringLiteral(''))));
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
updateStatement = t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.thisExpression(), t.identifier(hostProperty)), t.identifier('__v')));
|
|
171
|
+
}
|
|
172
|
+
const updateMethod = t.classMethod('method', t.identifier(`__updateHostBinding_${getterName}`), [], t.blockStatement([
|
|
173
|
+
t.variableDeclaration('const', [t.variableDeclarator(t.identifier('__v'), t.memberExpression(t.thisExpression(), t.identifier(getterName)))]),
|
|
174
|
+
updateStatement
|
|
175
|
+
]));
|
|
176
|
+
newMembers.push(updateMethod);
|
|
177
|
+
getterHostBindingUpdates.push(`__updateHostBinding_${getterName}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
decorators.splice(hostBindingIndex, 1);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (!memberPath.isClassProperty())
|
|
186
|
+
continue;
|
|
187
|
+
const propNode = memberPath.node;
|
|
188
|
+
const decorators = propNode.decorators ?? [];
|
|
189
|
+
const hostBindingDecoratorIndex = decorators.findIndex(dec => {
|
|
190
|
+
if (t.isCallExpression(dec.expression) && t.isIdentifier(dec.expression.callee)) {
|
|
191
|
+
return dec.expression.callee.name === 'HostBinding';
|
|
192
|
+
}
|
|
193
|
+
return false;
|
|
194
|
+
});
|
|
195
|
+
if (hostBindingDecoratorIndex >= 0) {
|
|
196
|
+
const hostBindingDecorator = decorators[hostBindingDecoratorIndex];
|
|
197
|
+
if (t.isCallExpression(hostBindingDecorator.expression)) {
|
|
198
|
+
const args = hostBindingDecorator.expression.arguments;
|
|
199
|
+
if (args.length > 0 && t.isStringLiteral(args[0]) && t.isIdentifier(propNode.key)) {
|
|
200
|
+
const hostProperty = args[0].value;
|
|
201
|
+
const propName = propNode.key.name;
|
|
202
|
+
const privateName = `__hostBinding_${propName}`;
|
|
203
|
+
const initialValue = propNode.value ?? t.identifier('undefined');
|
|
204
|
+
const privateField = t.classProperty(t.identifier(privateName), initialValue);
|
|
205
|
+
const getter = t.classMethod('get', t.identifier(propName), [], t.blockStatement([
|
|
206
|
+
t.returnStatement(t.memberExpression(t.thisExpression(), t.identifier(privateName)))
|
|
207
|
+
]));
|
|
208
|
+
let updateStatement = t.emptyStatement();
|
|
209
|
+
if (hostProperty.startsWith('class.')) {
|
|
210
|
+
const className = hostProperty.slice(6);
|
|
211
|
+
updateStatement = t.ifStatement(t.identifier('__v'), t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier('classList')), t.identifier('add')), [t.stringLiteral(className)])), t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier('classList')), t.identifier('remove')), [t.stringLiteral(className)])));
|
|
212
|
+
}
|
|
213
|
+
else if (hostProperty.startsWith('attr.')) {
|
|
214
|
+
const attrName = hostProperty.slice(5);
|
|
215
|
+
updateStatement = t.ifStatement(t.binaryExpression('!=', t.identifier('__v'), t.nullLiteral()), t.expressionStatement(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('setAttribute')), [
|
|
216
|
+
t.stringLiteral(attrName),
|
|
217
|
+
t.callExpression(t.identifier('String'), [t.identifier('__v')])
|
|
218
|
+
])), t.expressionStatement(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('removeAttribute')), [t.stringLiteral(attrName)])));
|
|
219
|
+
}
|
|
220
|
+
else if (hostProperty.startsWith('style.')) {
|
|
221
|
+
const styleProp = hostProperty.slice(6);
|
|
222
|
+
updateStatement = t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier('style')), t.identifier(styleProp)), t.logicalExpression('||', t.identifier('__v'), t.stringLiteral(''))));
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
updateStatement = t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.thisExpression(), t.identifier(hostProperty)), t.identifier('__v')));
|
|
226
|
+
}
|
|
227
|
+
const setter = t.classMethod('set', t.identifier(propName), [t.identifier('__v')], t.blockStatement([
|
|
228
|
+
t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.thisExpression(), t.identifier(privateName)), t.identifier('__v'))),
|
|
229
|
+
updateStatement
|
|
230
|
+
]));
|
|
231
|
+
newMembers.push(getter, setter);
|
|
232
|
+
propsToRemove.push(memberPath);
|
|
233
|
+
propertyHostBindingInits.push({ propName, privateName });
|
|
234
|
+
path.unshiftContainer('body', privateField);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
const viewChildDecoratorIndex = decorators.findIndex(dec => {
|
|
240
|
+
if (t.isCallExpression(dec.expression) && t.isIdentifier(dec.expression.callee)) {
|
|
241
|
+
return dec.expression.callee.name === 'ViewChild';
|
|
242
|
+
}
|
|
243
|
+
return false;
|
|
244
|
+
});
|
|
245
|
+
if (viewChildDecoratorIndex >= 0) {
|
|
246
|
+
const viewChildDecorator = decorators[viewChildDecoratorIndex];
|
|
247
|
+
if (t.isCallExpression(viewChildDecorator.expression)) {
|
|
248
|
+
const args = viewChildDecorator.expression.arguments;
|
|
249
|
+
if (args.length > 0 && t.isStringLiteral(args[0]) && t.isIdentifier(propNode.key)) {
|
|
250
|
+
const selector = args[0].value;
|
|
251
|
+
const propName = propNode.key.name;
|
|
252
|
+
const getter = t.classMethod('get', t.identifier(propName), [], t.blockStatement([
|
|
253
|
+
t.returnStatement(t.logicalExpression('||', t.callExpression(t.memberExpression(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('__getShadowRoot')), []), t.identifier('querySelector')), [t.stringLiteral(`[data-ref="${selector}"]`)]), t.callExpression(t.memberExpression(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('__getShadowRoot')), []), t.identifier('querySelector')), [t.stringLiteral(selector)])))
|
|
254
|
+
]));
|
|
255
|
+
newMembers.push(getter);
|
|
256
|
+
propsToRemove.push(memberPath);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
const reactiveDecoratorIndices = [];
|
|
262
|
+
for (const [idx, dec] of decorators.entries()) {
|
|
263
|
+
const name = t.isCallExpression(dec.expression) && t.isIdentifier(dec.expression.callee) ? dec.expression.callee.name : t.isIdentifier(dec.expression) ? dec.expression.name : null;
|
|
264
|
+
if (name === 'Reactive' || name === 'Input') {
|
|
265
|
+
reactiveDecoratorIndices.push(idx);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (reactiveDecoratorIndices.length === 0)
|
|
269
|
+
continue;
|
|
270
|
+
for (let i = reactiveDecoratorIndices.length - 1; i >= 0; i--) {
|
|
271
|
+
decorators.splice(reactiveDecoratorIndices[i], 1);
|
|
272
|
+
}
|
|
273
|
+
if (!t.isIdentifier(propNode.key))
|
|
274
|
+
continue;
|
|
275
|
+
const propName = propNode.key.name;
|
|
276
|
+
const privateName = `__${propName}`;
|
|
277
|
+
const initialValue = propNode.value ?? t.identifier('undefined');
|
|
278
|
+
state.needsPropertyImport = true;
|
|
279
|
+
state.reactiveProperties?.add(propName);
|
|
280
|
+
const privateField = t.classProperty(t.identifier(privateName), t.newExpression(t.identifier('Property'), [initialValue]));
|
|
281
|
+
const getter = t.classMethod('get', t.identifier(propName), [], t.blockStatement([
|
|
282
|
+
t.returnStatement(t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier(privateName)), t.identifier('getValue')), []))
|
|
283
|
+
]));
|
|
284
|
+
const setter = t.classMethod('set', t.identifier(propName), [t.identifier('__v')], t.blockStatement([
|
|
285
|
+
t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier(privateName)), t.identifier('setValue')), [t.identifier('__v')]))
|
|
286
|
+
]));
|
|
287
|
+
propsToRemove.push(memberPath);
|
|
288
|
+
newMembers.push(getter, setter);
|
|
289
|
+
privateFields.push(privateField);
|
|
290
|
+
}
|
|
291
|
+
for (const p of propsToRemove) {
|
|
292
|
+
p.remove();
|
|
293
|
+
}
|
|
294
|
+
for (const field of privateFields.reverse()) {
|
|
295
|
+
path.unshiftContainer('body', field);
|
|
296
|
+
}
|
|
297
|
+
for (const member of newMembers.reverse()) {
|
|
298
|
+
path.unshiftContainer('body', member);
|
|
299
|
+
}
|
|
300
|
+
if (pipeMethods.length > 0) {
|
|
301
|
+
const pipeProperties = pipeMethods.map(({ pipeName, methodName }) => t.objectProperty(t.identifier(pipeName), t.arrowFunctionExpression([t.restElement(t.identifier('args'))], t.callExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier(methodName)), t.identifier('apply')), [
|
|
302
|
+
t.thisExpression(),
|
|
303
|
+
t.identifier('args')
|
|
304
|
+
]))));
|
|
305
|
+
const pipesProperty = t.classProperty(t.identifier('__pipes'), t.objectExpression(pipeProperties));
|
|
306
|
+
path.unshiftContainer('body', pipesProperty);
|
|
307
|
+
}
|
|
308
|
+
const classDecl = path.parentPath;
|
|
309
|
+
const classNode = classDecl?.node;
|
|
310
|
+
const decorators = (classNode && 'decorators' in classNode ? classNode.decorators : null) ?? [];
|
|
311
|
+
const componentDecorator = decorators.find((dec) => t.isCallExpression(dec.expression) && t.isIdentifier(dec.expression.callee) && dec.expression.callee.name === 'Component');
|
|
312
|
+
if (componentDecorator && t.isCallExpression(componentDecorator.expression)) {
|
|
313
|
+
const args = componentDecorator.expression.arguments;
|
|
314
|
+
if (args.length > 0 && t.isObjectExpression(args[0])) {
|
|
315
|
+
const [configObj] = args;
|
|
316
|
+
const pipesProp = configObj.properties.find(p => t.isObjectProperty(p) && t.isIdentifier(p.key) && p.key.name === 'pipes');
|
|
317
|
+
if (pipesProp && t.isObjectProperty(pipesProp) && t.isArrayExpression(pipesProp.value)) {
|
|
318
|
+
const pipeClasses = pipesProp.value.elements.filter((el) => t.isIdentifier(el));
|
|
319
|
+
if (pipeClasses.length > 0) {
|
|
320
|
+
const simplePipeProperties = pipeClasses.map(pipeClass => t.objectProperty(t.memberExpression(pipeClass, t.identifier('__pipeName')), t.arrowFunctionExpression([
|
|
321
|
+
t.identifier('v'),
|
|
322
|
+
t.restElement(t.identifier('a'))
|
|
323
|
+
], t.callExpression(t.memberExpression(t.newExpression(pipeClass, []), t.identifier('transform')), [
|
|
324
|
+
t.identifier('v'),
|
|
325
|
+
t.spreadElement(t.identifier('a'))
|
|
326
|
+
])), true));
|
|
327
|
+
const pipesFromClasses = t.classProperty(t.identifier('__pipes'), t.objectExpression(simplePipeProperties));
|
|
328
|
+
path.unshiftContainer('body', pipesFromClasses);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const reactiveProps = state.reactiveProperties ?? new Set();
|
|
334
|
+
const constructorStatements = [];
|
|
335
|
+
for (const updateMethodName of getterHostBindingUpdates) {
|
|
336
|
+
constructorStatements.push(t.expressionStatement(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier(updateMethodName)), [])));
|
|
337
|
+
}
|
|
338
|
+
for (const { propName, privateName } of propertyHostBindingInits) {
|
|
339
|
+
constructorStatements.push(t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.thisExpression(), t.identifier(propName)), t.memberExpression(t.thisExpression(), t.identifier(privateName)))));
|
|
340
|
+
}
|
|
341
|
+
for (const { methodName, watchedProps: mProps } of watchMethods) {
|
|
342
|
+
constructorStatements.push(t.expressionStatement(t.unaryExpression('void', t.memberExpression(t.thisExpression(), t.identifier(methodName)))));
|
|
343
|
+
for (const prop of mProps) {
|
|
344
|
+
if (reactiveProps.has(prop)) {
|
|
345
|
+
constructorStatements.push(t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier(`__${prop}`)), t.identifier('onChange')), t.identifier('subscribe')), [t.arrowFunctionExpression([], t.callExpression(t.memberExpression(t.thisExpression(), t.identifier(methodName)), []))])));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
for (const { propName, watchedProps: wProps, callbackArg } of watchCalls) {
|
|
350
|
+
const iife = [
|
|
351
|
+
t.variableDeclaration('const', [t.variableDeclarator(t.identifier('__cb'), t.isExpression(callbackArg) ? callbackArg : t.identifier('undefined'))]),
|
|
352
|
+
t.variableDeclaration('const', [t.variableDeclarator(t.identifier('__subs'), t.arrayExpression([]))])
|
|
353
|
+
];
|
|
354
|
+
for (const prop of wProps) {
|
|
355
|
+
if (reactiveProps.has(prop)) {
|
|
356
|
+
iife.push(t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('__subs'), t.identifier('push')), [
|
|
357
|
+
t.callExpression(t.memberExpression(t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier(`__${prop}`)), t.identifier('onChange')), t.identifier('subscribe')), [t.identifier('__cb')])
|
|
358
|
+
])));
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
iife.push(t.expressionStatement(t.callExpression(t.identifier('__cb'), [])));
|
|
362
|
+
iife.push(t.returnStatement(t.objectExpression([
|
|
363
|
+
t.objectMethod('method', t.identifier('unsubscribe'), [], t.blockStatement([
|
|
364
|
+
t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('__subs'), t.identifier('forEach')), [
|
|
365
|
+
t.arrowFunctionExpression([t.identifier('s')], t.callExpression(t.memberExpression(t.identifier('s'), t.identifier('unsubscribe')), []))
|
|
366
|
+
]))
|
|
367
|
+
]))
|
|
368
|
+
])));
|
|
369
|
+
const watchClassProp = t.classProperty(t.identifier(propName), t.callExpression(t.arrowFunctionExpression([], t.blockStatement(iife)), []));
|
|
370
|
+
path.pushContainer('body', watchClassProp);
|
|
371
|
+
}
|
|
372
|
+
for (const { methodName, eventName } of hostListeners) {
|
|
373
|
+
const [baseEvent, ...modifiers] = eventName.split('.');
|
|
374
|
+
let handlerBody = t.emptyStatement();
|
|
375
|
+
if (modifiers.length > 0) {
|
|
376
|
+
const conditions = modifiers.map(mod => {
|
|
377
|
+
if (mod === 'enter')
|
|
378
|
+
return t.binaryExpression('===', t.memberExpression(t.identifier('__ev'), t.identifier('key')), t.stringLiteral('Enter'));
|
|
379
|
+
if (mod === 'escape')
|
|
380
|
+
return t.binaryExpression('===', t.memberExpression(t.identifier('__ev'), t.identifier('key')), t.stringLiteral('Escape'));
|
|
381
|
+
if (mod === 'space')
|
|
382
|
+
return t.binaryExpression('===', t.memberExpression(t.identifier('__ev'), t.identifier('key')), t.stringLiteral(' '));
|
|
383
|
+
return t.booleanLiteral(true);
|
|
384
|
+
});
|
|
385
|
+
const combinedCondition = conditions.reduce((acc, cond) => t.logicalExpression('&&', acc, cond), t.booleanLiteral(true));
|
|
386
|
+
handlerBody = t.ifStatement(combinedCondition, t.expressionStatement(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier(methodName)), [t.identifier('__ev')])));
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
handlerBody = t.expressionStatement(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier(methodName)), [t.identifier('__ev')]));
|
|
390
|
+
}
|
|
391
|
+
constructorStatements.push(t.expressionStatement(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('addEventListener')), [
|
|
392
|
+
t.stringLiteral(baseEvent),
|
|
393
|
+
t.arrowFunctionExpression([t.identifier('__ev')], t.blockStatement([handlerBody]))
|
|
394
|
+
])));
|
|
395
|
+
}
|
|
396
|
+
if (constructorStatements.length > 0) {
|
|
397
|
+
const constructor = path.node.body.find((m) => t.isClassMethod(m) && m.kind === 'constructor');
|
|
398
|
+
if (constructor) {
|
|
399
|
+
const superIndex = constructor.body.body.findIndex(stmt => t.isExpressionStatement(stmt) && t.isCallExpression(stmt.expression) && t.isSuper(stmt.expression.callee));
|
|
400
|
+
constructor.body.body.splice(superIndex >= 0 ? superIndex + 1 : 0, 0, ...constructorStatements);
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
path.unshiftContainer('body', t.classMethod('constructor', t.identifier('constructor'), [], t.blockStatement([
|
|
404
|
+
t.expressionStatement(t.callExpression(t.super(), [])), ...constructorStatements
|
|
405
|
+
])));
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
}
|
package/bin.d.ts
ADDED
package/bin.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Cli, parseArgs } from './Cli.js';
|
|
3
|
+
const { options, args } = parseArgs(process.argv.slice(2));
|
|
4
|
+
const cli = new Cli(options);
|
|
5
|
+
cli.run(args)
|
|
6
|
+
.catch((error) => {
|
|
7
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8
|
+
console.error(`Error: ${message}`);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Plugin } from 'esbuild';
|
|
2
|
+
export interface FluffPluginOptions {
|
|
3
|
+
srcDir: string;
|
|
4
|
+
minify?: boolean;
|
|
5
|
+
sourcemap?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function fluffPlugin(options: FluffPluginOptions): Plugin;
|
|
8
|
+
//# sourceMappingURL=fluff-esbuild-plugin.d.ts.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { ComponentCompiler } from './ComponentCompiler.js';
|
|
5
|
+
function findFluffSourcePath() {
|
|
6
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
7
|
+
const distDir = path.dirname(thisFile);
|
|
8
|
+
const cliPackageDir = path.dirname(distDir);
|
|
9
|
+
const packagesDir = path.dirname(cliPackageDir);
|
|
10
|
+
const fluffSrcPath = path.join(packagesDir, 'fluff', 'src');
|
|
11
|
+
if (fs.existsSync(path.join(fluffSrcPath, 'index.ts'))) {
|
|
12
|
+
return fluffSrcPath;
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
export function fluffPlugin(options) {
|
|
17
|
+
const compiler = new ComponentCompiler();
|
|
18
|
+
let componentsDiscovered = false;
|
|
19
|
+
const fluffSrcPath = findFluffSourcePath();
|
|
20
|
+
return {
|
|
21
|
+
name: 'fluff',
|
|
22
|
+
setup(build) {
|
|
23
|
+
build.onStart(async () => {
|
|
24
|
+
if (!componentsDiscovered) {
|
|
25
|
+
await compiler.discoverComponents(options.srcDir);
|
|
26
|
+
componentsDiscovered = true;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
build.onLoad({ filter: /\.component\.ts$/ }, async (args) => {
|
|
30
|
+
const result = await compiler.compileComponentForBundle(args.path, options.minify, options.sourcemap);
|
|
31
|
+
return {
|
|
32
|
+
contents: result.code,
|
|
33
|
+
loader: 'js',
|
|
34
|
+
resolveDir: path.dirname(args.path),
|
|
35
|
+
watchFiles: result.watchFiles
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
if (fluffSrcPath) {
|
|
39
|
+
build.onResolve({ filter: /^@fluffjs\/fluff$/ }, () => {
|
|
40
|
+
return { path: path.join(fluffSrcPath, 'index.ts') };
|
|
41
|
+
});
|
|
42
|
+
build.onResolve({ filter: /^@fluffjs\/fluff\// }, (args) => {
|
|
43
|
+
const subPath = args.path.replace('@fluffjs/fluff/', '');
|
|
44
|
+
return { path: path.join(fluffSrcPath, subPath + '.ts') };
|
|
45
|
+
});
|
|
46
|
+
build.onResolve({ filter: /\.js$/ }, (args) => {
|
|
47
|
+
if (args.resolveDir.includes(fluffSrcPath)) {
|
|
48
|
+
const tsPath = path.join(args.resolveDir, args.path.replace('.js', '.ts'));
|
|
49
|
+
if (args.path.includes('types/component')) {
|
|
50
|
+
return { path: args.path, external: true };
|
|
51
|
+
}
|
|
52
|
+
return { path: tsPath };
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { ComponentCompiler } from './ComponentCompiler.js';
|
|
2
|
+
export { TemplateParser } from './TemplateParser.js';
|
|
3
|
+
export { CodeGenerator } from './CodeGenerator.js';
|
|
4
|
+
export { Cli, parseArgs } from './Cli.js';
|
|
5
|
+
export type * from './types.js';
|
|
6
|
+
export type * from './types/FluffConfig.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fluffjs/cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./index.js",
|
|
6
|
+
"module": "./index.js",
|
|
7
|
+
"types": "./index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"fluff": "./bin.js"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
"./package.json": "./package.json",
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./index.d.ts",
|
|
15
|
+
"import": "./index.js",
|
|
16
|
+
"default": "./index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"*.js",
|
|
21
|
+
"*.d.ts",
|
|
22
|
+
"!*.tsbuildinfo"
|
|
23
|
+
],
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@babel/core": "^7.24.0",
|
|
26
|
+
"@babel/generator": "^7.24.0",
|
|
27
|
+
"@babel/parser": "^7.24.0",
|
|
28
|
+
"@babel/traverse": "^7.24.0",
|
|
29
|
+
"@babel/types": "^7.24.0",
|
|
30
|
+
"esbuild": "^0.20.0",
|
|
31
|
+
"html-minifier-terser": "^7.2.0",
|
|
32
|
+
"parse5": "^7.1.0",
|
|
33
|
+
"source-map": "^0.7.4",
|
|
34
|
+
"tslib": "^2.3.0"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
}
|
|
39
|
+
}
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface ComponentInfo {
|
|
2
|
+
className: string;
|
|
3
|
+
selector: string;
|
|
4
|
+
templatePath: string;
|
|
5
|
+
stylePath?: string;
|
|
6
|
+
filePath: string;
|
|
7
|
+
}
|
|
8
|
+
export interface TemplateBinding {
|
|
9
|
+
id: string;
|
|
10
|
+
type: 'text' | 'property' | 'event' | 'class' | 'style';
|
|
11
|
+
expression: string;
|
|
12
|
+
target?: string;
|
|
13
|
+
eventName?: string;
|
|
14
|
+
className?: string;
|
|
15
|
+
styleProp?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface SwitchCase {
|
|
18
|
+
value: string | null;
|
|
19
|
+
content: string;
|
|
20
|
+
fallthrough: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface ControlFlow {
|
|
23
|
+
id: string;
|
|
24
|
+
type: 'if' | 'for' | 'switch';
|
|
25
|
+
condition?: string;
|
|
26
|
+
ifContent?: string;
|
|
27
|
+
elseContent?: string;
|
|
28
|
+
iterator?: string;
|
|
29
|
+
iterable?: string;
|
|
30
|
+
trackBy?: string;
|
|
31
|
+
content?: string;
|
|
32
|
+
expression?: string;
|
|
33
|
+
cases?: SwitchCase[];
|
|
34
|
+
}
|
|
35
|
+
export interface ParsedTemplate {
|
|
36
|
+
html: string;
|
|
37
|
+
bindings: TemplateBinding[];
|
|
38
|
+
controlFlows: ControlFlow[];
|
|
39
|
+
templateRefs: string[];
|
|
40
|
+
}
|
|
41
|
+
export interface CompilerOptions {
|
|
42
|
+
srcDir: string;
|
|
43
|
+
distDir: string;
|
|
44
|
+
componentSelectors: Set<string>;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=types.d.ts.map
|
package/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|