dalila 1.9.4 → 1.9.5
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/dist/cli/check.js +1 -1
- package/dist/cli/index.js +5 -5
- package/dist/runtime/bind.js +106 -20
- package/package.json +1 -1
- package/scripts/dev-server.cjs +1 -1
package/README.md
CHANGED
package/dist/cli/check.js
CHANGED
|
@@ -520,7 +520,7 @@ function extractTemplateIdentifiers(html) {
|
|
|
520
520
|
i++;
|
|
521
521
|
}
|
|
522
522
|
// --- 2. Directive scanning (supports single and double quotes) ---
|
|
523
|
-
const DIRECTIVE_RE = /\b(d-each|d-virtual-each|d-virtual-height|d-virtual-item-height|d-virtual-overscan|d-if|d-when|d-match|d-html|d-attr-[a-zA-Z][\w-]*|d-on-[a-zA-Z][\w-]*|d-form-error|d-form|d-array)\s*=\s*(['"])([\s\S]*?)\2/g;
|
|
523
|
+
const DIRECTIVE_RE = /\b(d-each|d-virtual-each|d-virtual-height|d-virtual-item-height|d-virtual-overscan|d-if|d-when|d-match|d-html|d-attr-[a-zA-Z][\w-]*|d-bind-[a-zA-Z][\w-]*|d-on-[a-zA-Z][\w-]*|d-form-error|d-form|d-array)\s*=\s*(['"])([\s\S]*?)\2/g;
|
|
524
524
|
DIRECTIVE_RE.lastIndex = 0;
|
|
525
525
|
let match;
|
|
526
526
|
while ((match = DIRECTIVE_RE.exec(html))) {
|
package/dist/cli/index.js
CHANGED
|
@@ -9,7 +9,7 @@ const routeArgs = args.slice(2);
|
|
|
9
9
|
const WATCH_DEBOUNCE_MS = 120;
|
|
10
10
|
function showHelp() {
|
|
11
11
|
console.log(`
|
|
12
|
-
|
|
12
|
+
🐰✂️ Dalila CLI
|
|
13
13
|
|
|
14
14
|
Usage:
|
|
15
15
|
dalila routes generate [options] Generate routes + manifest from app file structure
|
|
@@ -32,7 +32,7 @@ Examples:
|
|
|
32
32
|
}
|
|
33
33
|
function showRoutesHelp() {
|
|
34
34
|
console.log(`
|
|
35
|
-
|
|
35
|
+
🐰✂️ Dalila CLI - Routes
|
|
36
36
|
|
|
37
37
|
Usage:
|
|
38
38
|
dalila routes generate [options] Generate routes + manifest from app file structure
|
|
@@ -52,7 +52,7 @@ Examples:
|
|
|
52
52
|
}
|
|
53
53
|
function showCheckHelp() {
|
|
54
54
|
console.log(`
|
|
55
|
-
|
|
55
|
+
🐰✂️ Dalila CLI - Check
|
|
56
56
|
|
|
57
57
|
Usage:
|
|
58
58
|
dalila check [path] [options] Static analysis of HTML templates
|
|
@@ -182,7 +182,7 @@ function resolveGenerateConfig(cliArgs, cwd = process.cwd()) {
|
|
|
182
182
|
async function generateRoutes(cliArgs) {
|
|
183
183
|
const { appDir, outputPath } = resolveGenerateConfig(cliArgs);
|
|
184
184
|
console.log('');
|
|
185
|
-
console.log('
|
|
185
|
+
console.log('🐰✂️ Dalila Routes Generator');
|
|
186
186
|
console.log('');
|
|
187
187
|
try {
|
|
188
188
|
await generateRoutesFile(appDir, outputPath);
|
|
@@ -243,7 +243,7 @@ function watchRoutes(cliArgs) {
|
|
|
243
243
|
process.exit(1);
|
|
244
244
|
}
|
|
245
245
|
console.log('');
|
|
246
|
-
console.log('
|
|
246
|
+
console.log('🐰✂️ Dalila Routes Watch');
|
|
247
247
|
console.log(` app: ${appDir}`);
|
|
248
248
|
console.log(` output: ${outputPath}`);
|
|
249
249
|
console.log('');
|
package/dist/runtime/bind.js
CHANGED
|
@@ -2068,14 +2068,76 @@ function bindAttrs(root, ctx, cleanups) {
|
|
|
2068
2068
|
// d-bind-* Directive (Two-way Binding)
|
|
2069
2069
|
// ============================================================================
|
|
2070
2070
|
/**
|
|
2071
|
-
* Bind all [d-bind
|
|
2072
|
-
* Two-way
|
|
2073
|
-
*
|
|
2071
|
+
* Bind all [d-bind-*] directives within root.
|
|
2072
|
+
* Two-way bindings:
|
|
2073
|
+
* - d-bind-value
|
|
2074
|
+
* - d-bind-checked
|
|
2075
|
+
*
|
|
2076
|
+
* One-way reactive property bindings:
|
|
2077
|
+
* - d-bind-readonly
|
|
2078
|
+
* - d-bind-disabled
|
|
2079
|
+
* - d-bind-maxlength
|
|
2080
|
+
* - d-bind-placeholder
|
|
2081
|
+
* - d-bind-pattern
|
|
2082
|
+
* - d-bind-multiple
|
|
2083
|
+
*
|
|
2084
|
+
* Optional transform/parse hooks:
|
|
2085
|
+
* - d-bind-transform="fnName" (signal -> view)
|
|
2086
|
+
* - d-bind-parse="fnName" (view -> signal)
|
|
2074
2087
|
*/
|
|
2075
2088
|
function bindTwoWay(root, ctx, cleanups) {
|
|
2076
|
-
const SUPPORTED = [
|
|
2077
|
-
|
|
2078
|
-
|
|
2089
|
+
const SUPPORTED = [
|
|
2090
|
+
{ attr: 'd-bind-value', prop: 'value', twoWay: true },
|
|
2091
|
+
{ attr: 'd-bind-checked', prop: 'checked', twoWay: true },
|
|
2092
|
+
{ attr: 'd-bind-readonly', prop: 'readOnly', twoWay: false },
|
|
2093
|
+
{ attr: 'd-bind-disabled', prop: 'disabled', twoWay: false },
|
|
2094
|
+
{ attr: 'd-bind-maxlength', prop: 'maxLength', twoWay: false },
|
|
2095
|
+
{ attr: 'd-bind-placeholder', prop: 'placeholder', twoWay: false },
|
|
2096
|
+
{ attr: 'd-bind-pattern', prop: 'pattern', twoWay: false },
|
|
2097
|
+
{ attr: 'd-bind-multiple', prop: 'multiple', twoWay: false },
|
|
2098
|
+
];
|
|
2099
|
+
const BOOLEAN_PROPS = new Set(['checked', 'readOnly', 'disabled', 'multiple']);
|
|
2100
|
+
const STRING_PROPS = new Set(['value', 'placeholder', 'pattern']);
|
|
2101
|
+
const applyBoundProp = (el, prop, value) => {
|
|
2102
|
+
if (!(prop in el))
|
|
2103
|
+
return;
|
|
2104
|
+
if (BOOLEAN_PROPS.has(prop)) {
|
|
2105
|
+
el[prop] = !!value;
|
|
2106
|
+
return;
|
|
2107
|
+
}
|
|
2108
|
+
if (STRING_PROPS.has(prop)) {
|
|
2109
|
+
el[prop] = value == null ? '' : String(value);
|
|
2110
|
+
return;
|
|
2111
|
+
}
|
|
2112
|
+
if (prop === 'maxLength') {
|
|
2113
|
+
if (value == null || value === '') {
|
|
2114
|
+
el.maxLength = -1;
|
|
2115
|
+
return;
|
|
2116
|
+
}
|
|
2117
|
+
const parsed = Number(value);
|
|
2118
|
+
el.maxLength = Number.isFinite(parsed) ? Math.max(0, Math.floor(parsed)) : -1;
|
|
2119
|
+
return;
|
|
2120
|
+
}
|
|
2121
|
+
el[prop] = value;
|
|
2122
|
+
};
|
|
2123
|
+
const resolveFunctionBinding = (el, attrName) => {
|
|
2124
|
+
const fnBindingName = normalizeBinding(el.getAttribute(attrName));
|
|
2125
|
+
if (!fnBindingName)
|
|
2126
|
+
return null;
|
|
2127
|
+
const fnBinding = ctx[fnBindingName];
|
|
2128
|
+
if (fnBinding === undefined) {
|
|
2129
|
+
warn(`${attrName}: "${fnBindingName}" not found in context`);
|
|
2130
|
+
return null;
|
|
2131
|
+
}
|
|
2132
|
+
const resolved = isSignal(fnBinding) ? fnBinding() : fnBinding;
|
|
2133
|
+
if (typeof resolved !== 'function') {
|
|
2134
|
+
warn(`${attrName}: "${fnBindingName}" must be a function (or signal-of-function)`);
|
|
2135
|
+
return null;
|
|
2136
|
+
}
|
|
2137
|
+
return resolved;
|
|
2138
|
+
};
|
|
2139
|
+
for (const directive of SUPPORTED) {
|
|
2140
|
+
const attr = directive.attr;
|
|
2079
2141
|
const elements = qsaIncludingRoot(root, `[${attr}]`);
|
|
2080
2142
|
for (const el of elements) {
|
|
2081
2143
|
const bindingName = normalizeBinding(el.getAttribute(attr));
|
|
@@ -2083,31 +2145,55 @@ function bindTwoWay(root, ctx, cleanups) {
|
|
|
2083
2145
|
continue;
|
|
2084
2146
|
const binding = ctx[bindingName];
|
|
2085
2147
|
if (!isSignal(binding)) {
|
|
2086
|
-
warn(
|
|
2148
|
+
warn(`${attr}: "${bindingName}" must be a signal`);
|
|
2087
2149
|
continue;
|
|
2088
2150
|
}
|
|
2089
2151
|
const writable = isWritableSignal(binding);
|
|
2090
|
-
if (!writable) {
|
|
2091
|
-
warn(
|
|
2152
|
+
if (directive.twoWay && !writable) {
|
|
2153
|
+
warn(`${attr}: "${bindingName}" is read-only (inbound updates disabled)`);
|
|
2092
2154
|
}
|
|
2093
2155
|
el.removeAttribute(attr);
|
|
2156
|
+
const transformFn = resolveFunctionBinding(el, 'd-bind-transform');
|
|
2157
|
+
const parseFn = resolveFunctionBinding(el, 'd-bind-parse');
|
|
2158
|
+
if (directive.twoWay) {
|
|
2159
|
+
el.removeAttribute('d-bind-transform');
|
|
2160
|
+
el.removeAttribute('d-bind-parse');
|
|
2161
|
+
}
|
|
2094
2162
|
// Outbound: signal → DOM
|
|
2095
|
-
const isBoolean = prop === 'checked';
|
|
2096
2163
|
bindEffect(el, () => {
|
|
2097
|
-
const
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2164
|
+
const rawValue = binding();
|
|
2165
|
+
let value = rawValue;
|
|
2166
|
+
if (transformFn) {
|
|
2167
|
+
try {
|
|
2168
|
+
value = transformFn(rawValue, el);
|
|
2169
|
+
}
|
|
2170
|
+
catch (err) {
|
|
2171
|
+
warn(`d-bind-transform: "${bindingName}" failed (${err.message || String(err)})`);
|
|
2172
|
+
value = rawValue;
|
|
2173
|
+
}
|
|
2103
2174
|
}
|
|
2175
|
+
applyBoundProp(el, directive.prop, value);
|
|
2104
2176
|
});
|
|
2105
2177
|
// Inbound: DOM → signal
|
|
2106
|
-
if (writable) {
|
|
2107
|
-
const eventName = el.tagName === 'SELECT' ||
|
|
2178
|
+
if (directive.twoWay && writable) {
|
|
2179
|
+
const eventName = el.tagName === 'SELECT' || directive.prop === 'checked'
|
|
2180
|
+
? 'change'
|
|
2181
|
+
: 'input';
|
|
2108
2182
|
const handler = () => {
|
|
2109
|
-
const
|
|
2110
|
-
|
|
2183
|
+
const rawValue = directive.prop === 'checked'
|
|
2184
|
+
? el.checked
|
|
2185
|
+
: el.value;
|
|
2186
|
+
let nextValue = rawValue;
|
|
2187
|
+
if (parseFn) {
|
|
2188
|
+
try {
|
|
2189
|
+
nextValue = parseFn(rawValue, el);
|
|
2190
|
+
}
|
|
2191
|
+
catch (err) {
|
|
2192
|
+
warn(`d-bind-parse: "${bindingName}" failed (${err.message || String(err)})`);
|
|
2193
|
+
nextValue = rawValue;
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
binding.set(nextValue);
|
|
2111
2197
|
};
|
|
2112
2198
|
el.addEventListener(eventName, handler);
|
|
2113
2199
|
cleanups.push(() => el.removeEventListener(eventName, handler));
|
package/package.json
CHANGED
package/scripts/dev-server.cjs
CHANGED
|
@@ -1047,7 +1047,7 @@ startKeepalive();
|
|
|
1047
1047
|
|
|
1048
1048
|
server.listen(port, () => {
|
|
1049
1049
|
console.log('');
|
|
1050
|
-
console.log('
|
|
1050
|
+
console.log(' 🐰✂️ Dalila dev server');
|
|
1051
1051
|
console.log(` http://localhost:${port}`);
|
|
1052
1052
|
console.log('');
|
|
1053
1053
|
});
|