svelte-origin 1.0.0-next.21 → 1.0.0-next.23

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.

Potentially problematic release.


This version of svelte-origin might be problematic. Click here for more details.

@@ -1969,6 +1969,7 @@ function transformOriginDefinition(content, svelteImports) {
1969
1969
  let attrsContent = "";
1970
1970
  let contentWithoutAttrs = bodyContent;
1971
1971
  let attrPropertyName = "props";
1972
+ let propsRef = null;
1972
1973
  if (attrsMatch) {
1973
1974
  attrPropertyName = attrsMatch[1];
1974
1975
  const attrsStart = attrsMatch.index;
@@ -1999,13 +2000,31 @@ function transformOriginDefinition(content, svelteImports) {
1999
2000
  }
2000
2001
  contentWithoutAttrs = bodyContent.slice(0, attrsStart) + bodyContent.slice(cutEnd);
2001
2002
  }
2003
+ } else {
2004
+ const externalRefMatch = bodyContent.match(/(\w+)\s*:\s*(\w+)\s*,/);
2005
+ if (externalRefMatch) {
2006
+ const propName = externalRefMatch[1];
2007
+ const refName = externalRefMatch[2];
2008
+ if ((propName === "props" || propName === "attrs") && /^[A-Z]/.test(refName) && !["$state", "$derived", "$effect", "$bindable"].includes(refName)) {
2009
+ attrPropertyName = propName;
2010
+ propsRef = refName;
2011
+ const refStart = externalRefMatch.index;
2012
+ const refEnd = refStart + externalRefMatch[0].length;
2013
+ contentWithoutAttrs = bodyContent.slice(0, refStart) + bodyContent.slice(refEnd);
2014
+ }
2015
+ }
2002
2016
  }
2003
2017
  const attrDetails = parseAttrsSource(attrsContent);
2004
2018
  const { body: createFnBody, attachments } = transformOriginBody(contentWithoutAttrs.trim(), parents, attrDetails, attrPropertyName, svelteImports);
2005
2019
  const parentsCode = parents.length > 0 ? `[${parents.join(", ")}]` : "undefined";
2006
2020
  let configCode = `{
2007
2021
  __attrSchema: ${attrSchemaCode},
2008
- __parents: ${parentsCode},
2022
+ __parents: ${parentsCode},`;
2023
+ if (propsRef) {
2024
+ configCode += `
2025
+ __propsRef: ${propsRef},`;
2026
+ }
2027
+ configCode += `
2009
2028
  __create: (__inputAttrs${parents.length > 0 ? ", __super" : ""}) => {
2010
2029
  ${createFnBody}
2011
2030
  }`;
@@ -2240,6 +2259,73 @@ function parseOriginSchemaFromSource(source, exportName) {
2240
2259
  return compiledResult;
2241
2260
  return null;
2242
2261
  }
2262
+ function parseAttrsSchemaFromSource(source, exportName) {
2263
+ const sourceResult = parseSourceAttrs(source, exportName);
2264
+ if (sourceResult)
2265
+ return sourceResult;
2266
+ const compiledResult = parseCompiledAttrs(source, exportName);
2267
+ if (compiledResult)
2268
+ return compiledResult;
2269
+ return null;
2270
+ }
2271
+ function parseSourceAttrs(source, exportName) {
2272
+ const exportPattern = new RegExp(`export\\s+(?:const|var|let)\\s+${exportName}\\s*=\\s*\\$attrs\\s*\\(`, "s");
2273
+ const match = exportPattern.exec(source);
2274
+ if (!match)
2275
+ return null;
2276
+ const attrsStart = match.index + match[0].length - 1;
2277
+ const attrsEnd = findMatchingBracket(source, attrsStart);
2278
+ if (attrsEnd === -1)
2279
+ return null;
2280
+ let attrsContent = source.slice(attrsStart + 1, attrsEnd).trim();
2281
+ let parentNames = [];
2282
+ if (attrsContent.startsWith("[")) {
2283
+ const closeBracket = findMatchingBracket(attrsContent, 0, "[", "]");
2284
+ if (closeBracket !== -1) {
2285
+ const parentsContent = attrsContent.slice(1, closeBracket);
2286
+ parentNames = parentsContent.split(",").map((p) => p.trim()).filter((p) => p && /^\w+$/.test(p));
2287
+ let i = closeBracket + 1;
2288
+ while (i < attrsContent.length && /[\s,]/.test(attrsContent[i]))
2289
+ i++;
2290
+ attrsContent = attrsContent.slice(i);
2291
+ }
2292
+ }
2293
+ if (!attrsContent.startsWith("{")) {
2294
+ return { attrs: [], parentNames };
2295
+ }
2296
+ const objEnd = findMatchingBracket(attrsContent, 0, "{", "}");
2297
+ if (objEnd === -1)
2298
+ return { attrs: [], parentNames };
2299
+ const objContent = attrsContent.slice(1, objEnd);
2300
+ const attrs = parseAttrsContent(objContent);
2301
+ return { attrs, parentNames };
2302
+ }
2303
+ function parseCompiledAttrs(source, exportName) {
2304
+ const exportPattern = new RegExp(`export\\s+(?:const|var|let)\\s+${exportName}\\s*=\\s*__createAttrs\\s*\\(\\s*\\{`, "s");
2305
+ const match = exportPattern.exec(source);
2306
+ if (!match)
2307
+ return null;
2308
+ const braceStart = match.index + match[0].length - 1;
2309
+ const braceEnd = findMatchingBracket(source, braceStart, "{", "}");
2310
+ if (braceEnd === -1)
2311
+ return null;
2312
+ const configContent = source.slice(braceStart + 1, braceEnd);
2313
+ const schemaMatch = configContent.match(/__attrSchema\s*:\s*\{/);
2314
+ if (!schemaMatch)
2315
+ return { attrs: [], parentNames: [] };
2316
+ const schemaStart = schemaMatch.index + schemaMatch[0].length - 1;
2317
+ const schemaEnd = findMatchingBracket(configContent, schemaStart, "{", "}");
2318
+ if (schemaEnd === -1)
2319
+ return { attrs: [], parentNames: [] };
2320
+ const schemaContent = configContent.slice(schemaStart + 1, schemaEnd);
2321
+ const attrs = parseCompiledAttrSchema(schemaContent);
2322
+ const parentsMatch = configContent.match(/__parents\s*:\s*\[([^\]]*)\]/);
2323
+ let parentNames = [];
2324
+ if (parentsMatch && parentsMatch[1].trim()) {
2325
+ parentNames = parentsMatch[1].split(",").map((p) => p.trim()).filter((p) => p && /^\w+$/.test(p));
2326
+ }
2327
+ return { attrs, parentNames };
2328
+ }
2243
2329
  function parseSourceOrigin(source, exportName) {
2244
2330
  const exportPattern = new RegExp(`export\\s+(?:const|var|let)\\s+${exportName}\\s*=\\s*\\$origin\\s*\\(`, "s");
2245
2331
  const match = exportPattern.exec(source);
@@ -2265,6 +2351,13 @@ function parseSourceOrigin(source, exportName) {
2265
2351
  }
2266
2352
  const attrsMatch = bodyContent.match(/(\w+)\s*:\s*\$attrs\s*\(\s*\{/);
2267
2353
  if (!attrsMatch) {
2354
+ const externalRefMatch = bodyContent.match(/(?:props|attrs)\s*:\s*([A-Z]\w*)\b/);
2355
+ if (externalRefMatch) {
2356
+ const propsRef = externalRefMatch[1];
2357
+ if (!["Object", "Array", "String", "Number", "Boolean"].includes(propsRef)) {
2358
+ return { attrs: [], parentNames, propsRef };
2359
+ }
2360
+ }
2268
2361
  return { attrs: [], parentNames };
2269
2362
  }
2270
2363
  const attrsStart = attrsMatch.index;
@@ -2301,7 +2394,9 @@ function parseCompiledOrigin(source, exportName) {
2301
2394
  if (parentsMatch && parentsMatch[1].trim()) {
2302
2395
  parentNames = parentsMatch[1].split(",").map((p) => p.trim()).filter((p) => p && /^\w+$/.test(p));
2303
2396
  }
2304
- return { attrs, parentNames };
2397
+ const propsRefMatch = configContent.match(/__propsRef\s*:\s*(\w+)/);
2398
+ const propsRef = propsRefMatch ? propsRefMatch[1] : undefined;
2399
+ return { attrs, parentNames, propsRef };
2305
2400
  }
2306
2401
  function parseCompiledAttrSchema(content) {
2307
2402
  const result = [];
@@ -2741,7 +2836,34 @@ function getElementTypeImport(element) {
2741
2836
  }
2742
2837
 
2743
2838
  // src/transform/attrs-for-transform.ts
2744
- function transformAttrsForCalls(s, source, neededImports) {
2839
+ function generateReactiveAttrsWrapper(attrs) {
2840
+ const parts = [];
2841
+ for (const attr of attrs) {
2842
+ parts.push(`get ${attr.key}() { return ${attr.key} }`);
2843
+ if (attr.bindable) {
2844
+ parts.push(`set ${attr.key}(v) { ${attr.key} = v }`);
2845
+ }
2846
+ }
2847
+ return `{ ${parts.join(", ")} }`;
2848
+ }
2849
+ function generateAttrsForMerge(attrs) {
2850
+ const parts = [];
2851
+ for (const attr of attrs) {
2852
+ if (attr.bindable) {
2853
+ if (attr.hasDefault) {
2854
+ parts.push(`${attr.key} = $bindable(${attr.defaultValue})`);
2855
+ } else {
2856
+ parts.push(`${attr.key} = $bindable()`);
2857
+ }
2858
+ } else if (attr.hasDefault) {
2859
+ parts.push(`${attr.key} = ${attr.defaultValue}`);
2860
+ } else {
2861
+ parts.push(attr.key);
2862
+ }
2863
+ }
2864
+ return parts.join(", ");
2865
+ }
2866
+ function transformAttrsForCallsSync(s, source, neededImports) {
2745
2867
  let changed = false;
2746
2868
  const existingPropsMatch = source.match(/let\s*\{([^}]*)\}\s*(?::\s*[^=]+)?\s*=\s*\$props\s*\(\s*\)/);
2747
2869
  let restVarName = null;
@@ -2807,6 +2929,111 @@ function transformAttrsForCalls(s, source, neededImports) {
2807
2929
  }
2808
2930
  return changed;
2809
2931
  }
2932
+ async function transformAttrsForCalls(s, source, neededImports, schemaResolver) {
2933
+ let changed = false;
2934
+ const existingPropsMatch = source.match(/let\s*\{([^}]*)\}\s*(?::\s*[^=]+)?\s*=\s*\$props\s*\(\s*\)/);
2935
+ let restVarName = null;
2936
+ let existingPropsStart = -1;
2937
+ let existingDestructureContent = "";
2938
+ if (existingPropsMatch) {
2939
+ existingPropsStart = existingPropsMatch.index;
2940
+ existingDestructureContent = existingPropsMatch[1];
2941
+ const restMatch = existingDestructureContent.match(/\.\.\.\s*(\w+)\s*$/);
2942
+ if (restMatch) {
2943
+ restVarName = restMatch[1];
2944
+ }
2945
+ }
2946
+ const declarations = findVariableDeclarationsWithMacro(source, "$attrs.for");
2947
+ for (const decl of declarations) {
2948
+ const { varName, startIndex, macroOpenParen } = decl;
2949
+ const closeParenIndex = findMatchingBracket(source, macroOpenParen);
2950
+ if (closeParenIndex === -1)
2951
+ continue;
2952
+ const arg = source.slice(macroOpenParen + 1, closeParenIndex).trim();
2953
+ const endIndex = closeParenIndex + 1;
2954
+ const stringMatch = arg.match(/^['"](\w+)['"]$/);
2955
+ if (stringMatch) {
2956
+ const element = stringMatch[1];
2957
+ const typeImport = getElementTypeImport(element);
2958
+ if (existingPropsMatch && existingPropsStart !== -1) {
2959
+ if (!restVarName) {
2960
+ restVarName = "___restAttrs";
2961
+ const newDestructure = existingDestructureContent.trimEnd() ? `${existingDestructureContent.trimEnd()}, ...${restVarName}` : `...${restVarName}`;
2962
+ const braceEnd = source.indexOf("}", existingPropsStart + 5);
2963
+ if (braceEnd !== -1) {
2964
+ s.overwrite(existingPropsStart + 5, braceEnd, ` ${newDestructure} `);
2965
+ }
2966
+ }
2967
+ s.overwrite(startIndex, endIndex, `let ${varName} = ${restVarName} as ${typeImport}`);
2968
+ } else {
2969
+ const expansion = `let { ...${varName} } = $props<${typeImport}>()`;
2970
+ s.overwrite(startIndex, endIndex, expansion);
2971
+ }
2972
+ } else {
2973
+ const factoryName = arg;
2974
+ let transformed = false;
2975
+ if (schemaResolver) {
2976
+ const importPath = findImportPath(source, factoryName);
2977
+ if (importPath) {
2978
+ const schema = await schemaResolver.resolve(importPath, factoryName);
2979
+ if (schema && schema.attrs.length > 0) {
2980
+ if (existingPropsMatch && existingPropsStart !== -1) {
2981
+ const additionalAttrs = generateAttrsForMerge(schema.attrs);
2982
+ const restMatch = existingDestructureContent.match(/,?\s*\.\.\.\s*(\w+)\s*$/);
2983
+ let baseContent = existingDestructureContent;
2984
+ let restPart = "";
2985
+ if (restMatch) {
2986
+ restPart = restMatch[0];
2987
+ baseContent = existingDestructureContent.slice(0, restMatch.index);
2988
+ }
2989
+ const mergedDestructure = baseContent.trimEnd() ? `${baseContent.trimEnd()}, ${additionalAttrs}${restPart}` : `${additionalAttrs}${restPart}`;
2990
+ const braceEnd = source.indexOf("}", existingPropsStart + 5);
2991
+ if (braceEnd !== -1) {
2992
+ s.overwrite(existingPropsStart + 5, braceEnd, ` ${mergedDestructure} `);
2993
+ }
2994
+ const wrapper = generateReactiveAttrsWrapper(schema.attrs);
2995
+ s.overwrite(startIndex, endIndex, `let ${varName} = __attrsFor(${factoryName}, ${wrapper})`);
2996
+ transformed = true;
2997
+ } else {
2998
+ const propsDestructure = generatePropsDestructuring(schema.attrs, factoryName);
2999
+ const wrapper = generateReactiveAttrsWrapper(schema.attrs);
3000
+ const expansion = [
3001
+ propsDestructure,
3002
+ `let ${varName} = __attrsFor(${factoryName}, ${wrapper})`
3003
+ ].join(`
3004
+ `);
3005
+ s.overwrite(startIndex, endIndex, expansion);
3006
+ transformed = true;
3007
+ }
3008
+ }
3009
+ }
3010
+ }
3011
+ if (!transformed) {
3012
+ if (existingPropsMatch && existingPropsStart !== -1) {
3013
+ if (!restVarName) {
3014
+ restVarName = "___restAttrs";
3015
+ const newDestructure = existingDestructureContent.trimEnd() ? `${existingDestructureContent.trimEnd()}, ...${restVarName}` : `...${restVarName}`;
3016
+ const braceEnd = source.indexOf("}", existingPropsStart + 5);
3017
+ if (braceEnd !== -1) {
3018
+ s.overwrite(existingPropsStart + 5, braceEnd, ` ${newDestructure} `);
3019
+ }
3020
+ }
3021
+ s.overwrite(startIndex, endIndex, `let ${varName} = __attrsFor(${factoryName}, ${restVarName})`);
3022
+ } else {
3023
+ const expansion = [
3024
+ `let ___attrs: $attrs.Of<typeof ${factoryName}> = $props()`,
3025
+ `let ${varName} = __attrsFor(${factoryName}, ___attrs)`
3026
+ ].join(`
3027
+ `);
3028
+ s.overwrite(startIndex, endIndex, expansion);
3029
+ }
3030
+ }
3031
+ neededImports.add("__attrsFor");
3032
+ }
3033
+ changed = true;
3034
+ }
3035
+ return changed;
3036
+ }
2810
3037
 
2811
3038
  // src/transform/core.ts
2812
3039
  function transformScript(source, options = {}) {
@@ -2832,7 +3059,7 @@ function transformScript(source, options = {}) {
2832
3059
  propsTypeDeclaration = result.propsTypeDeclaration;
2833
3060
  }
2834
3061
  if (isComponent) {
2835
- changed = transformAttrsForCalls(s, source, neededImports) || changed;
3062
+ changed = transformAttrsForCallsSync(s, source, neededImports) || changed;
2836
3063
  }
2837
3064
  const needsPropsInjection = propsTypeDeclaration && propsTypeDeclaration.length > 0;
2838
3065
  if (isComponent && (neededImports.size > 0 || needsPropsInjection)) {
@@ -2903,7 +3130,7 @@ async function transformScriptAsync(source, options) {
2903
3130
  propsTypeDeclaration = result.propsTypeDeclaration;
2904
3131
  }
2905
3132
  if (isComponent) {
2906
- changed = transformAttrsForCalls(s, source, neededImports) || changed;
3133
+ changed = await transformAttrsForCalls(s, source, neededImports, schemaResolver) || changed;
2907
3134
  }
2908
3135
  const needsPropsInjectionAsync = propsTypeDeclaration && propsTypeDeclaration.length > 0;
2909
3136
  if (isComponent && (neededImports.size > 0 || needsPropsInjectionAsync)) {
@@ -2973,7 +3200,7 @@ async function transformScriptContent(source, options) {
2973
3200
  propsTypeDeclaration = result.propsTypeDeclaration;
2974
3201
  }
2975
3202
  if (isComponent) {
2976
- changed = transformAttrsForCalls(s, source, neededImports) || changed;
3203
+ changed = await transformAttrsForCalls(s, source, neededImports, schemaResolver) || changed;
2977
3204
  }
2978
3205
  if (neededImports.size > 0) {
2979
3206
  const importStatement = generateRuntimeImport([...neededImports]);
@@ -3468,6 +3695,60 @@ async function resolveSchema(importPath, exportName, importerId, cache, debug, a
3468
3695
  }
3469
3696
  }
3470
3697
  }
3698
+ if (schema.propsRef) {
3699
+ const propsRefImportPath = findImportPath(source, schema.propsRef);
3700
+ let propsRefSchema = null;
3701
+ if (propsRefImportPath) {
3702
+ if (debug) {
3703
+ console.log(`[svelte-origin-preprocess] Resolving propsRef: ${schema.propsRef} from import ${propsRefImportPath}`);
3704
+ }
3705
+ propsRefSchema = await resolveAttrsSchema(propsRefImportPath, schema.propsRef, filePath, cache, debug, aliases);
3706
+ } else {
3707
+ if (debug) {
3708
+ console.log(`[svelte-origin-preprocess] Resolving propsRef: ${schema.propsRef} from same file`);
3709
+ }
3710
+ propsRefSchema = parseAttrsSchemaFromSource(source, schema.propsRef);
3711
+ if (propsRefSchema && propsRefSchema.parentNames.length > 0) {
3712
+ const resolvedParentAttrs = [];
3713
+ for (const parentName of propsRefSchema.parentNames) {
3714
+ const parentImportPath = findImportPath(source, parentName);
3715
+ let parentSchema = null;
3716
+ if (parentImportPath) {
3717
+ parentSchema = await resolveAttrsSchema(parentImportPath, parentName, filePath, cache, debug, aliases);
3718
+ } else {
3719
+ parentSchema = parseAttrsSchemaFromSource(source, parentName);
3720
+ }
3721
+ if (parentSchema) {
3722
+ for (const attr of parentSchema.attrs) {
3723
+ const existingIndex = resolvedParentAttrs.findIndex((a) => a.key === attr.key);
3724
+ if (existingIndex === -1) {
3725
+ resolvedParentAttrs.push(attr);
3726
+ }
3727
+ }
3728
+ }
3729
+ }
3730
+ for (const attr of propsRefSchema.attrs) {
3731
+ const existingIndex = resolvedParentAttrs.findIndex((a) => a.key === attr.key);
3732
+ if (existingIndex !== -1) {
3733
+ resolvedParentAttrs[existingIndex] = attr;
3734
+ } else {
3735
+ resolvedParentAttrs.push(attr);
3736
+ }
3737
+ }
3738
+ propsRefSchema = { attrs: resolvedParentAttrs, parentNames: propsRefSchema.parentNames };
3739
+ }
3740
+ }
3741
+ if (propsRefSchema) {
3742
+ for (const attr of propsRefSchema.attrs) {
3743
+ const existingIndex = mergedAttrs.findIndex((a) => a.key === attr.key);
3744
+ if (existingIndex === -1) {
3745
+ mergedAttrs.push(attr);
3746
+ } else {
3747
+ mergedAttrs[existingIndex] = attr;
3748
+ }
3749
+ }
3750
+ }
3751
+ }
3471
3752
  for (const attr of schema.attrs) {
3472
3753
  const existingIndex = mergedAttrs.findIndex((a) => a.key === attr.key);
3473
3754
  if (existingIndex !== -1) {
@@ -3493,6 +3774,75 @@ async function resolveSchema(importPath, exportName, importerId, cache, debug, a
3493
3774
  return null;
3494
3775
  }
3495
3776
  }
3777
+ async function resolveAttrsSchema(importPath, exportName, importerId, cache, debug, aliases) {
3778
+ const cacheKey = `attrs::${importPath}::${exportName}`;
3779
+ if (cache.has(cacheKey)) {
3780
+ if (debug) {
3781
+ console.log(`[svelte-origin-preprocess] Cache hit for ${cacheKey}`);
3782
+ }
3783
+ return cache.get(cacheKey) || null;
3784
+ }
3785
+ try {
3786
+ const importerDir = dirname2(importerId);
3787
+ if (debug) {
3788
+ console.log(`[svelte-origin-preprocess] Resolving attrs: ${exportName} from ${importPath}`);
3789
+ }
3790
+ const resolvedPath = resolveImportPath(importPath, aliases, importerDir);
3791
+ if (!resolvedPath) {
3792
+ cache.set(cacheKey, null);
3793
+ return null;
3794
+ }
3795
+ const filePath = resolveFilePath(resolvedPath);
3796
+ if (!filePath) {
3797
+ cache.set(cacheKey, null);
3798
+ return null;
3799
+ }
3800
+ const source = readFileSync2(filePath, "utf-8");
3801
+ const schema = parseAttrsSchemaFromSource(source, exportName);
3802
+ if (!schema) {
3803
+ cache.set(cacheKey, null);
3804
+ return null;
3805
+ }
3806
+ const mergedAttrs = [];
3807
+ for (const parentName of schema.parentNames) {
3808
+ const parentImportPath = findImportPath(source, parentName);
3809
+ if (parentImportPath) {
3810
+ const parentSchema = await resolveAttrsSchema(parentImportPath, parentName, filePath, cache, debug, aliases);
3811
+ if (parentSchema) {
3812
+ for (const attr of parentSchema.attrs) {
3813
+ const existingIndex = mergedAttrs.findIndex((a) => a.key === attr.key);
3814
+ if (existingIndex === -1) {
3815
+ mergedAttrs.push(attr);
3816
+ }
3817
+ }
3818
+ }
3819
+ }
3820
+ }
3821
+ for (const attr of schema.attrs) {
3822
+ const existingIndex = mergedAttrs.findIndex((a) => a.key === attr.key);
3823
+ if (existingIndex !== -1) {
3824
+ mergedAttrs[existingIndex] = attr;
3825
+ } else {
3826
+ mergedAttrs.push(attr);
3827
+ }
3828
+ }
3829
+ const mergedSchema = {
3830
+ attrs: mergedAttrs,
3831
+ parentNames: schema.parentNames
3832
+ };
3833
+ if (debug) {
3834
+ console.log(`[svelte-origin-preprocess] Resolved attrs schema for ${exportName}:`, mergedSchema.attrs.map((a) => a.key));
3835
+ }
3836
+ cache.set(cacheKey, mergedSchema);
3837
+ return mergedSchema;
3838
+ } catch (error) {
3839
+ if (debug) {
3840
+ console.error(`[svelte-origin-preprocess] Error resolving attrs schema for ${importPath}:`, error);
3841
+ }
3842
+ cache.set(cacheKey, null);
3843
+ return null;
3844
+ }
3845
+ }
3496
3846
  function svelteOriginPreprocess(options = {}) {
3497
3847
  const {
3498
3848
  debug = false,
@@ -3473,34 +3473,22 @@ function __attrsFor(factory, rawAttrs) {
3473
3473
  const factoryWithSchema = factory;
3474
3474
  const schema = getMergedAttrSchema(factoryWithSchema);
3475
3475
  const wrapper = {};
3476
- const localValues = {};
3477
- const hasLocalValue = {};
3478
3476
  const defineExposedProp = (key2, info) => {
3479
3477
  if (Object.prototype.hasOwnProperty.call(wrapper, key2))
3480
3478
  return;
3481
- const base = {
3479
+ const descriptor = {
3482
3480
  get() {
3483
- if (hasLocalValue[key2])
3484
- return localValues[key2];
3485
3481
  return rawAttrs[key2];
3486
3482
  },
3487
3483
  enumerable: true,
3488
3484
  configurable: true
3489
3485
  };
3490
3486
  if (info.bindable) {
3491
- Object.defineProperty(wrapper, key2, {
3492
- ...base,
3493
- set(value) {
3494
- localValues[key2] = value;
3495
- hasLocalValue[key2] = true;
3496
- try {
3497
- rawAttrs[key2] = value;
3498
- } catch {}
3499
- }
3500
- });
3501
- } else {
3502
- Object.defineProperty(wrapper, key2, base);
3487
+ descriptor.set = function(value) {
3488
+ rawAttrs[key2] = value;
3489
+ };
3503
3490
  }
3491
+ Object.defineProperty(wrapper, key2, descriptor);
3504
3492
  };
3505
3493
  for (const [key2, info] of Object.entries(schema)) {
3506
3494
  const parentValue = rawAttrs[key2];
@@ -3521,8 +3509,6 @@ function __attrsFor(factory, rawAttrs) {
3521
3509
  return new Proxy(wrapper, {
3522
3510
  get(target, prop2) {
3523
3511
  if (typeof prop2 === "string" && prop2 in schema) {
3524
- if (hasLocalValue[prop2])
3525
- return localValues[prop2];
3526
3512
  return rawAttrs[prop2];
3527
3513
  }
3528
3514
  if (prop2 in target)
@@ -3531,15 +3517,11 @@ function __attrsFor(factory, rawAttrs) {
3531
3517
  },
3532
3518
  set(target, prop2, value) {
3533
3519
  if (typeof prop2 === "string" && prop2 in schema) {
3534
- localValues[prop2] = value;
3535
- hasLocalValue[prop2] = true;
3536
3520
  if (!Object.prototype.hasOwnProperty.call(wrapper, prop2)) {
3537
3521
  defineExposedProp(prop2, schema[prop2]);
3538
3522
  }
3539
3523
  if (schema[prop2].bindable) {
3540
- try {
3541
- rawAttrs[prop2] = value;
3542
- } catch {}
3524
+ rawAttrs[prop2] = value;
3543
3525
  }
3544
3526
  return true;
3545
3527
  }
@@ -3551,18 +3533,10 @@ function __attrsFor(factory, rawAttrs) {
3551
3533
  }
3552
3534
  try {
3553
3535
  rawAttrs[prop2] = value;
3554
- } catch {
3555
- if (typeof prop2 === "string") {
3556
- localValues[prop2] = value;
3557
- hasLocalValue[prop2] = true;
3558
- }
3559
- }
3536
+ } catch {}
3560
3537
  return true;
3561
3538
  },
3562
3539
  has(target, prop2) {
3563
- if (typeof prop2 === "string" && prop2 in schema) {
3564
- return prop2 in target || hasLocalValue[prop2];
3565
- }
3566
3540
  return prop2 in target || prop2 in rawAttrs;
3567
3541
  },
3568
3542
  ownKeys() {
@@ -6,12 +6,13 @@
6
6
  * - $attrs.for('input') - get typed element attrs
7
7
  */
8
8
  import type MagicString from 'magic-string';
9
+ import { type SchemaResolver } from './schema';
9
10
  /**
10
- * Transform $attrs.for(X) calls.
11
- * Handles both origin factories ($attrs.for(OriginFactory)) and element names ($attrs.for('input'))
11
+ * Transform $attrs.for(X) calls (sync fallback version).
12
12
  *
13
- * If $props() already exists, we extend the existing destructuring with rest syntax.
14
- * Otherwise, we generate a new $props() call.
13
+ * This version always uses the fallback approach since no schema resolver is provided.
14
+ * Fallback generates a raw $props() proxy which does NOT support two-way binding
15
+ * through parent components (the props proxy is read-only for non-destructured props).
15
16
  *
16
17
  * Supports all TypeScript declaration patterns:
17
18
  * - let x = $attrs.for(Factory)
@@ -21,4 +22,34 @@ import type MagicString from 'magic-string';
21
22
  * - export let x = $attrs.for(Factory)
22
23
  * - export const x: Type = $attrs.for('input')
23
24
  */
24
- export declare function transformAttrsForCalls(s: MagicString, source: string, neededImports: Set<string>): boolean;
25
+ export declare function transformAttrsForCallsSync(s: MagicString, source: string, neededImports: Set<string>): boolean;
26
+ /**
27
+ * Transform $attrs.for(X) calls (async version with schema resolution).
28
+ *
29
+ * When schema is available via static resolution:
30
+ * ```svelte
31
+ * let childAttrs = $attrs.for(Counter)
32
+ * ```
33
+ * Becomes:
34
+ * ```svelte
35
+ * let { label = 'Counter', count = $bindable(0), step = 1 }: $attrs.Of<typeof Counter> = $props()
36
+ * let childAttrs = __attrsFor(Counter, { get label() { return label }, get count() { return count }, set count(v) { count = v }, get step() { return step } })
37
+ * ```
38
+ *
39
+ * This approach works because:
40
+ * 1. Props are destructured with $bindable(), making them writable
41
+ * 2. The getter/setter wrapper passes reactive accessors to __attrsFor
42
+ * 3. When child components update bindable props, the setters propagate changes back
43
+ *
44
+ * Fallback (when schema isn't statically known):
45
+ * ```svelte
46
+ * let ___attrs: $attrs.Of<typeof Counter> = $props()
47
+ * let childAttrs = __attrsFor(Counter, ___attrs)
48
+ * ```
49
+ *
50
+ * @param s MagicString instance for source manipulation
51
+ * @param source Original source code
52
+ * @param neededImports Set to track required runtime imports
53
+ * @param schemaResolver Optional resolver for static schema extraction
54
+ */
55
+ export declare function transformAttrsForCalls(s: MagicString, source: string, neededImports: Set<string>, schemaResolver?: SchemaResolver): Promise<boolean>;
@@ -19,6 +19,8 @@ export interface ParsedAttrInfo {
19
19
  export interface ParsedOriginSchema {
20
20
  attrs: ParsedAttrInfo[];
21
21
  parentNames: string[];
22
+ /** Reference to external $attrs factory (e.g., 'TextAttrs' when props: TextAttrs is used) */
23
+ propsRef?: string;
22
24
  }
23
25
  /**
24
26
  * Parse an origin's schema from its source code.
@@ -40,6 +42,21 @@ export interface ParsedOriginSchema {
40
42
  * @returns Parsed schema or null if not found
41
43
  */
42
44
  export declare function parseOriginSchemaFromSource(source: string, exportName: string): ParsedOriginSchema | null;
45
+ /**
46
+ * Parse an $attrs factory's schema from its source code.
47
+ *
48
+ * Looks for patterns like:
49
+ * ```ts
50
+ * export const TextAttrs = $attrs({ ... })
51
+ * export const TextAttrs = $attrs([BaseAttrs], { ... })
52
+ * ```
53
+ *
54
+ * Or compiled:
55
+ * ```js
56
+ * export var TextAttrs = __createAttrs({ __attrSchema: {...}, __parents: [...] })
57
+ * ```
58
+ */
59
+ export declare function parseAttrsSchemaFromSource(source: string, exportName: string): ParsedOriginSchema | null;
43
60
  /**
44
61
  * Generate $props() destructuring code from parsed attrs
45
62
  *