svelte2tsx 0.7.24 → 0.7.25

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.
Files changed (3) hide show
  1. package/index.js +421 -133
  2. package/index.mjs +421 -133
  3. package/package.json +1 -1
package/index.js CHANGED
@@ -4166,17 +4166,17 @@ function eventMapEntryToString([eventName, expression]) {
4166
4166
  * Creates a hidden folder in the user's node_modules if `hiddenFolderPath` is provided.
4167
4167
  */
4168
4168
  function get_global_types(tsSystem, isSvelte3, sveltePath, typesPath, hiddenFolderPath) {
4169
- const svelteHtmlPath = isSvelte3 ? undefined : path.join(sveltePath, 'svelte-html.d.ts');
4170
- const svelteHtmlPathExists = svelteHtmlPath && tsSystem.fileExists(svelteHtmlPath);
4171
- const svelteHtmlFile = svelteHtmlPathExists ? svelteHtmlPath : './svelte-jsx-v4.d.ts';
4169
+ let svelteHtmlPath = isSvelte3 ? undefined : path.join(sveltePath, 'svelte-html.d.ts');
4170
+ svelteHtmlPath =
4171
+ svelteHtmlPath && tsSystem.fileExists(svelteHtmlPath) ? svelteHtmlPath : undefined;
4172
4172
  let svelteTsxFiles;
4173
4173
  if (isSvelte3) {
4174
4174
  svelteTsxFiles = ['./svelte-shims.d.ts', './svelte-jsx.d.ts', './svelte-native-jsx.d.ts'];
4175
4175
  }
4176
4176
  else {
4177
4177
  svelteTsxFiles = ['./svelte-shims-v4.d.ts', './svelte-native-jsx.d.ts'];
4178
- if (!svelteHtmlPathExists) {
4179
- svelteTsxFiles.push(svelteHtmlPath);
4178
+ if (!svelteHtmlPath) {
4179
+ svelteTsxFiles.push('./svelte-jsx-v4.d.ts');
4180
4180
  }
4181
4181
  }
4182
4182
  svelteTsxFiles = svelteTsxFiles.map((f) => tsSystem.resolvePath(path.resolve(typesPath, f)));
@@ -4203,9 +4203,16 @@ function get_global_types(tsSystem, isSvelte3, sveltePath, typesPath, hiddenFold
4203
4203
  for (const f of svelteTsxFiles) {
4204
4204
  const hiddenFile = path.resolve(hiddenPath, path.basename(f));
4205
4205
  const existing = tsSystem.readFile(hiddenFile);
4206
- const toWrite = tsSystem.readFile(f) || '';
4206
+ const toWrite = tsSystem.readFile(f);
4207
+ if (!toWrite) {
4208
+ throw new Error(`Could not read file: ${f}`);
4209
+ }
4207
4210
  if (existing !== toWrite) {
4208
4211
  tsSystem.writeFile(hiddenFile, toWrite);
4212
+ // TS doesn't throw an error if the file wasn't written
4213
+ if (!tsSystem.fileExists(hiddenFile)) {
4214
+ throw new Error(`Could not write file: ${hiddenFile}`);
4215
+ }
4209
4216
  }
4210
4217
  newFiles.push(hiddenFile);
4211
4218
  }
@@ -4214,8 +4221,8 @@ function get_global_types(tsSystem, isSvelte3, sveltePath, typesPath, hiddenFold
4214
4221
  }
4215
4222
  catch (e) { }
4216
4223
  }
4217
- if (svelteHtmlPathExists) {
4218
- svelteTsxFiles.push(tsSystem.resolvePath(path.resolve(typesPath, svelteHtmlFile)));
4224
+ if (svelteHtmlPath) {
4225
+ svelteTsxFiles.push(tsSystem.resolvePath(path.resolve(typesPath, svelteHtmlPath)));
4219
4226
  }
4220
4227
  return svelteTsxFiles;
4221
4228
  }
@@ -4612,6 +4619,270 @@ function getCurrentPrepends(str, index) {
4612
4619
  return ((_a = str.__prepends__) === null || _a === void 0 ? void 0 : _a.get(index)) || [];
4613
4620
  }
4614
4621
 
4622
+ /**
4623
+ * Collects all imports and module-level declarations to then find out which interfaces/types are hoistable.
4624
+ */
4625
+ class HoistableInterfaces {
4626
+ constructor() {
4627
+ this.import_value_set = new Set();
4628
+ this.import_type_set = new Set();
4629
+ this.interface_map = new Map();
4630
+ this.props_interface = {
4631
+ name: '',
4632
+ node: null,
4633
+ type_deps: new Set(),
4634
+ value_deps: new Set()
4635
+ };
4636
+ }
4637
+ analyzeModuleScriptNode(node) {
4638
+ // Handle Import Declarations
4639
+ if (ts.isImportDeclaration(node) && node.importClause) {
4640
+ const is_type_only = node.importClause.isTypeOnly;
4641
+ if (node.importClause.namedBindings &&
4642
+ ts.isNamedImports(node.importClause.namedBindings)) {
4643
+ node.importClause.namedBindings.elements.forEach((element) => {
4644
+ const import_name = element.name.text;
4645
+ if (is_type_only || element.isTypeOnly) {
4646
+ this.import_type_set.add(import_name);
4647
+ }
4648
+ else {
4649
+ this.import_value_set.add(import_name);
4650
+ }
4651
+ });
4652
+ }
4653
+ // Handle default imports
4654
+ if (node.importClause.name) {
4655
+ const default_import = node.importClause.name.text;
4656
+ if (is_type_only) {
4657
+ this.import_type_set.add(default_import);
4658
+ }
4659
+ else {
4660
+ this.import_value_set.add(default_import);
4661
+ }
4662
+ }
4663
+ // Handle namespace imports
4664
+ if (node.importClause.namedBindings &&
4665
+ ts.isNamespaceImport(node.importClause.namedBindings)) {
4666
+ const namespace_import = node.importClause.namedBindings.name.text;
4667
+ if (is_type_only) {
4668
+ this.import_type_set.add(namespace_import);
4669
+ }
4670
+ else {
4671
+ this.import_value_set.add(namespace_import);
4672
+ }
4673
+ }
4674
+ }
4675
+ // Handle top-level declarations
4676
+ if (ts.isVariableStatement(node)) {
4677
+ node.declarationList.declarations.forEach((declaration) => {
4678
+ if (ts.isIdentifier(declaration.name)) {
4679
+ this.import_value_set.add(declaration.name.text);
4680
+ }
4681
+ });
4682
+ }
4683
+ if (ts.isFunctionDeclaration(node) && node.name) {
4684
+ this.import_value_set.add(node.name.text);
4685
+ }
4686
+ if (ts.isClassDeclaration(node) && node.name) {
4687
+ this.import_value_set.add(node.name.text);
4688
+ }
4689
+ if (ts.isEnumDeclaration(node)) {
4690
+ this.import_value_set.add(node.name.text);
4691
+ }
4692
+ if (ts.isTypeAliasDeclaration(node)) {
4693
+ this.import_type_set.add(node.name.text);
4694
+ }
4695
+ if (ts.isInterfaceDeclaration(node)) {
4696
+ this.import_type_set.add(node.name.text);
4697
+ }
4698
+ }
4699
+ analyzeInstanceScriptNode(node) {
4700
+ var _a, _b, _c, _d;
4701
+ // Handle Import Declarations
4702
+ if (ts.isImportDeclaration(node) && node.importClause) {
4703
+ const is_type_only = node.importClause.isTypeOnly;
4704
+ if (node.importClause.namedBindings &&
4705
+ ts.isNamedImports(node.importClause.namedBindings)) {
4706
+ node.importClause.namedBindings.elements.forEach((element) => {
4707
+ const import_name = element.name.text;
4708
+ if (is_type_only) {
4709
+ this.import_type_set.add(import_name);
4710
+ }
4711
+ else {
4712
+ this.import_value_set.add(import_name);
4713
+ }
4714
+ });
4715
+ }
4716
+ // Handle default imports
4717
+ if (node.importClause.name) {
4718
+ const default_import = node.importClause.name.text;
4719
+ if (is_type_only) {
4720
+ this.import_type_set.add(default_import);
4721
+ }
4722
+ else {
4723
+ this.import_value_set.add(default_import);
4724
+ }
4725
+ }
4726
+ // Handle namespace imports
4727
+ if (node.importClause.namedBindings &&
4728
+ ts.isNamespaceImport(node.importClause.namedBindings)) {
4729
+ const namespace_import = node.importClause.namedBindings.name.text;
4730
+ if (is_type_only) {
4731
+ this.import_type_set.add(namespace_import);
4732
+ }
4733
+ else {
4734
+ this.import_value_set.add(namespace_import);
4735
+ }
4736
+ }
4737
+ }
4738
+ // Handle Interface Declarations
4739
+ if (ts.isInterfaceDeclaration(node)) {
4740
+ const interface_name = node.name.text;
4741
+ const type_dependencies = new Set();
4742
+ const value_dependencies = new Set();
4743
+ const generics = (_b = (_a = node.typeParameters) === null || _a === void 0 ? void 0 : _a.map((param) => param.name.text)) !== null && _b !== void 0 ? _b : [];
4744
+ node.members.forEach((member) => {
4745
+ if (ts.isPropertySignature(member) && member.type) {
4746
+ this.collectTypeDependencies(member.type, type_dependencies, value_dependencies, generics);
4747
+ }
4748
+ else if (ts.isIndexSignatureDeclaration(member)) {
4749
+ this.collectTypeDependencies(member.type, type_dependencies, value_dependencies, generics);
4750
+ member.parameters.forEach((param) => {
4751
+ this.collectTypeDependencies(param.type, type_dependencies, value_dependencies, generics);
4752
+ });
4753
+ }
4754
+ });
4755
+ this.interface_map.set(interface_name, {
4756
+ type_deps: type_dependencies,
4757
+ value_deps: value_dependencies,
4758
+ node
4759
+ });
4760
+ }
4761
+ // Handle Type Alias Declarations
4762
+ if (ts.isTypeAliasDeclaration(node)) {
4763
+ const alias_name = node.name.text;
4764
+ const type_dependencies = new Set();
4765
+ const value_dependencies = new Set();
4766
+ const generics = (_d = (_c = node.typeParameters) === null || _c === void 0 ? void 0 : _c.map((param) => param.name.text)) !== null && _d !== void 0 ? _d : [];
4767
+ this.collectTypeDependencies(node.type, type_dependencies, value_dependencies, generics);
4768
+ this.interface_map.set(alias_name, {
4769
+ type_deps: type_dependencies,
4770
+ value_deps: value_dependencies,
4771
+ node
4772
+ });
4773
+ }
4774
+ }
4775
+ analyze$propsRune(node) {
4776
+ var _a, _b;
4777
+ if (((_a = node.initializer.typeArguments) === null || _a === void 0 ? void 0 : _a.length) > 0 || node.type) {
4778
+ const generic_arg = ((_b = node.initializer.typeArguments) === null || _b === void 0 ? void 0 : _b[0]) || node.type;
4779
+ if (ts.isTypeReferenceNode(generic_arg)) {
4780
+ const name = this.getEntityNameText(generic_arg.typeName);
4781
+ const interface_node = this.interface_map.get(name);
4782
+ if (interface_node) {
4783
+ this.props_interface.name = name;
4784
+ this.props_interface.type_deps = interface_node.type_deps;
4785
+ this.props_interface.value_deps = interface_node.value_deps;
4786
+ }
4787
+ }
4788
+ else {
4789
+ this.props_interface.name = '$$ComponentProps';
4790
+ this.props_interface.node = generic_arg;
4791
+ this.collectTypeDependencies(generic_arg, this.props_interface.type_deps, this.props_interface.value_deps, []);
4792
+ }
4793
+ }
4794
+ }
4795
+ /**
4796
+ * Traverses the AST to collect import statements and top-level interfaces,
4797
+ * then determines which interfaces can be hoisted.
4798
+ * @param source_file The TypeScript source file to analyze.
4799
+ * @returns An object containing sets of value imports, type imports, and hoistable interfaces.
4800
+ */
4801
+ determineHoistableInterfaces() {
4802
+ const hoistable_interfaces = new Map();
4803
+ let progress = true;
4804
+ while (progress) {
4805
+ progress = false;
4806
+ for (const [interface_name, deps] of this.interface_map.entries()) {
4807
+ if (hoistable_interfaces.has(interface_name)) {
4808
+ continue;
4809
+ }
4810
+ const can_hoist = [...deps.type_deps, ...deps.value_deps].every((dep) => {
4811
+ return (this.import_type_set.has(dep) ||
4812
+ this.import_value_set.has(dep) ||
4813
+ hoistable_interfaces.has(dep));
4814
+ });
4815
+ if (can_hoist) {
4816
+ hoistable_interfaces.set(interface_name, deps.node);
4817
+ progress = true;
4818
+ }
4819
+ }
4820
+ }
4821
+ if (this.props_interface.name === '$$ComponentProps') {
4822
+ const can_hoist = [
4823
+ ...this.props_interface.type_deps,
4824
+ ...this.props_interface.value_deps
4825
+ ].every((dep) => {
4826
+ return (this.import_type_set.has(dep) ||
4827
+ this.import_value_set.has(dep) ||
4828
+ hoistable_interfaces.has(dep));
4829
+ });
4830
+ if (can_hoist) {
4831
+ hoistable_interfaces.set(this.props_interface.name, this.props_interface.node);
4832
+ }
4833
+ }
4834
+ return hoistable_interfaces;
4835
+ }
4836
+ /**
4837
+ * Moves all interfaces that can be hoisted to the top of the script, if the $props rune's type is hoistable.
4838
+ */
4839
+ moveHoistableInterfaces(str, astOffset, scriptStart) {
4840
+ if (!this.props_interface.name)
4841
+ return;
4842
+ const hoistable = this.determineHoistableInterfaces();
4843
+ if (hoistable.has(this.props_interface.name)) {
4844
+ for (const [, node] of hoistable) {
4845
+ str.move(node.pos + astOffset, node.end + astOffset, scriptStart);
4846
+ }
4847
+ }
4848
+ }
4849
+ /**
4850
+ * Collects type and value dependencies from a given TypeNode.
4851
+ * @param type_node The TypeNode to analyze.
4852
+ * @param type_dependencies The set to collect type dependencies into.
4853
+ * @param value_dependencies The set to collect value dependencies into.
4854
+ */
4855
+ collectTypeDependencies(type_node, type_dependencies, value_dependencies, generics) {
4856
+ const walk = (node) => {
4857
+ if (ts.isTypeReferenceNode(node)) {
4858
+ const type_name = this.getEntityNameText(node.typeName);
4859
+ if (!generics.includes(type_name)) {
4860
+ type_dependencies.add(type_name);
4861
+ }
4862
+ }
4863
+ else if (ts.isTypeQueryNode(node)) {
4864
+ // Handle 'typeof' expressions: e.g., foo: typeof bar
4865
+ value_dependencies.add(this.getEntityNameText(node.exprName));
4866
+ }
4867
+ ts.forEachChild(node, walk);
4868
+ };
4869
+ walk(type_node);
4870
+ }
4871
+ /**
4872
+ * Retrieves the full text of an EntityName (handles nested names).
4873
+ * @param entity_name The EntityName to extract text from.
4874
+ * @returns The full name as a string.
4875
+ */
4876
+ getEntityNameText(entity_name) {
4877
+ if (ts.isIdentifier(entity_name)) {
4878
+ return entity_name.text;
4879
+ }
4880
+ else {
4881
+ return this.getEntityNameText(entity_name.left) + '.' + entity_name.right.text;
4882
+ }
4883
+ }
4884
+ }
4885
+
4615
4886
  function is$$PropsDeclaration(node) {
4616
4887
  return isInterfaceOrTypeDeclaration(node) && node.name.text === '$$Props';
4617
4888
  }
@@ -4623,6 +4894,7 @@ class ExportedNames {
4623
4894
  this.isTsFile = isTsFile;
4624
4895
  this.isSvelte5Plus = isSvelte5Plus;
4625
4896
  this.isRunes = isRunes;
4897
+ this.hoistableInterfaces = new HoistableInterfaces();
4626
4898
  this.usesAccessors = false;
4627
4899
  /**
4628
4900
  * Uses the `$$Props` type
@@ -4637,7 +4909,9 @@ class ExportedNames {
4637
4909
  * If using TS, this returns the generic string, if using JS, returns the `@type {..}` string.
4638
4910
  */
4639
4911
  this.$props = {
4912
+ /** The JSDoc type; not set when TS type exists */
4640
4913
  comment: '',
4914
+ /** The TS type */
4641
4915
  type: '',
4642
4916
  bindings: []
4643
4917
  };
@@ -4741,10 +5015,12 @@ class ExportedNames {
4741
5015
  }
4742
5016
  }
4743
5017
  }
5018
+ // Easy mode: User uses TypeScript and typed the $props() rune
4744
5019
  if (((_a = node.initializer.typeArguments) === null || _a === void 0 ? void 0 : _a.length) > 0 || node.type) {
5020
+ this.hoistableInterfaces.analyze$propsRune(node);
4745
5021
  const generic_arg = ((_b = node.initializer.typeArguments) === null || _b === void 0 ? void 0 : _b[0]) || node.type;
4746
5022
  const generic = generic_arg.getText();
4747
- if (!generic.includes('{')) {
5023
+ if (ts.isTypeReferenceNode(generic_arg)) {
4748
5024
  this.$props.type = generic;
4749
5025
  }
4750
5026
  else {
@@ -4758,14 +5034,26 @@ class ExportedNames {
4758
5034
  // so that semantic tokens ignore it, preventing an overlap of tokens
4759
5035
  surroundWithIgnoreComments(this.$props.type));
4760
5036
  }
5037
+ return;
4761
5038
  }
4762
- else {
4763
- if (!this.isTsFile) {
4764
- const text = node.getSourceFile().getFullText();
4765
- let start = -1;
4766
- let comment;
4767
- // reverse because we want to look at the last comment before the node first
4768
- for (const c of [...(ts.getLeadingCommentRanges(text, node.pos) || [])].reverse()) {
5039
+ // Hard mode: User uses JSDoc or didn't type the $props() rune
5040
+ if (!this.isTsFile) {
5041
+ const text = node.getSourceFile().getFullText();
5042
+ let start = -1;
5043
+ let comment;
5044
+ // reverse because we want to look at the last comment before the node first
5045
+ for (const c of [...(ts.getLeadingCommentRanges(text, node.pos) || [])].reverse()) {
5046
+ const potential_match = text.substring(c.pos, c.end);
5047
+ if (/@type\b/.test(potential_match)) {
5048
+ comment = potential_match;
5049
+ start = c.pos + this.astOffset;
5050
+ break;
5051
+ }
5052
+ }
5053
+ if (!comment) {
5054
+ for (const c of [
5055
+ ...(ts.getLeadingCommentRanges(text, node.parent.pos) || []).reverse()
5056
+ ]) {
4769
5057
  const potential_match = text.substring(c.pos, c.end);
4770
5058
  if (/@type\b/.test(potential_match)) {
4771
5059
  comment = potential_match;
@@ -4773,127 +5061,115 @@ class ExportedNames {
4773
5061
  break;
4774
5062
  }
4775
5063
  }
4776
- if (!comment) {
4777
- for (const c of [
4778
- ...(ts.getLeadingCommentRanges(text, node.parent.pos) || []).reverse()
4779
- ]) {
4780
- const potential_match = text.substring(c.pos, c.end);
4781
- if (/@type\b/.test(potential_match)) {
4782
- comment = potential_match;
4783
- start = c.pos + this.astOffset;
4784
- break;
4785
- }
4786
- }
4787
- }
4788
- if (comment && /\/\*\*[^@]*?@type\s*{\s*{.*}\s*}\s*\*\//.test(comment)) {
4789
- // Create a virtual type alias for the unnamed generic and reuse it for the props return type
4790
- // so that rename, find references etc works seamlessly across components
4791
- this.$props.comment = '/** @type {$$ComponentProps} */';
4792
- const type_start = this.str.original.indexOf('@type', start);
4793
- this.str.overwrite(type_start, type_start + 5, '@typedef');
4794
- const end = this.str.original.indexOf('*/', start);
4795
- this.str.overwrite(end, end + 2, ' $$ComponentProps */' + this.$props.comment);
4796
- }
4797
- else {
4798
- // Complex comment or simple `@type {AType}` comment which we just use as-is.
4799
- // For the former this means things like rename won't work properly across components.
4800
- this.$props.comment = comment || '';
4801
- }
4802
5064
  }
4803
- if (this.$props.comment) {
4804
- return;
5065
+ if (comment && /\/\*\*[^@]*?@type\s*{\s*{.*}\s*}\s*\*\//.test(comment)) {
5066
+ // Create a virtual type alias for the unnamed generic and reuse it for the props return type
5067
+ // so that rename, find references etc works seamlessly across components
5068
+ this.$props.comment = '/** @type {$$ComponentProps} */';
5069
+ const type_start = this.str.original.indexOf('@type', start);
5070
+ this.str.overwrite(type_start, type_start + 5, '@typedef');
5071
+ const end = this.str.original.indexOf('*/', start);
5072
+ this.str.overwrite(end, end + 2, ' $$ComponentProps */' + this.$props.comment);
4805
5073
  }
4806
- // Do a best-effort to extract the props from the object literal
4807
- let propsStr = '';
4808
- let withUnknown = false;
4809
- let props = [];
4810
- const isKitRouteFile = internalHelpers.isKitRouteFile(this.basename);
4811
- const isKitLayoutFile = isKitRouteFile && this.basename.includes('layout');
4812
- if (ts.isObjectBindingPattern(node.name)) {
4813
- for (const element of node.name.elements) {
4814
- if (!ts.isIdentifier(element.name) ||
4815
- (element.propertyName && !ts.isIdentifier(element.propertyName)) ||
4816
- !!element.dotDotDotToken) {
4817
- withUnknown = true;
4818
- }
4819
- else {
4820
- const name = element.propertyName
4821
- ? element.propertyName.text
4822
- : element.name.text;
4823
- if (isKitRouteFile) {
4824
- if (name === 'data') {
4825
- props.push(`data: import('./$types.js').${isKitLayoutFile ? 'LayoutData' : 'PageData'}`);
4826
- }
4827
- if (name === 'form' && !isKitLayoutFile) {
4828
- props.push(`form: import('./$types.js').ActionData`);
4829
- }
4830
- }
4831
- else if (element.initializer) {
4832
- const initializer = ts.isCallExpression(element.initializer) &&
4833
- ts.isIdentifier(element.initializer.expression) &&
4834
- element.initializer.expression.text === '$bindable'
4835
- ? element.initializer.arguments[0]
4836
- : element.initializer;
4837
- const type = !initializer
4838
- ? 'unknown'
4839
- : ts.isAsExpression(initializer)
4840
- ? initializer.type.getText()
4841
- : ts.isStringLiteral(initializer)
4842
- ? 'string'
4843
- : ts.isNumericLiteral(initializer)
4844
- ? 'number'
4845
- : initializer.kind === ts.SyntaxKind.TrueKeyword ||
4846
- initializer.kind === ts.SyntaxKind.FalseKeyword
4847
- ? 'boolean'
4848
- : ts.isIdentifier(initializer) &&
4849
- initializer.text !== 'undefined'
4850
- ? `typeof ${initializer.text}`
4851
- : ts.isArrowFunction(initializer)
4852
- ? 'Function'
4853
- : ts.isObjectLiteralExpression(initializer)
4854
- ? 'Record<string, unknown>'
4855
- : ts.isArrayLiteralExpression(initializer)
4856
- ? 'unknown[]'
4857
- : 'unknown';
4858
- props.push(`${name}?: ${type}`);
5074
+ else {
5075
+ // Complex comment or simple `@type {AType}` comment which we just use as-is.
5076
+ // For the former this means things like rename won't work properly across components.
5077
+ this.$props.comment = comment || '';
5078
+ }
5079
+ }
5080
+ if (this.$props.comment) {
5081
+ // User uses JsDoc
5082
+ return;
5083
+ }
5084
+ // Do a best-effort to extract the props from the object literal
5085
+ let propsStr = '';
5086
+ let withUnknown = false;
5087
+ let props = [];
5088
+ const isKitRouteFile = internalHelpers.isKitRouteFile(this.basename);
5089
+ const isKitLayoutFile = isKitRouteFile && this.basename.includes('layout');
5090
+ if (ts.isObjectBindingPattern(node.name)) {
5091
+ for (const element of node.name.elements) {
5092
+ if (!ts.isIdentifier(element.name) ||
5093
+ (element.propertyName && !ts.isIdentifier(element.propertyName)) ||
5094
+ !!element.dotDotDotToken) {
5095
+ withUnknown = true;
5096
+ }
5097
+ else {
5098
+ const name = element.propertyName
5099
+ ? element.propertyName.text
5100
+ : element.name.text;
5101
+ if (isKitRouteFile) {
5102
+ if (name === 'data') {
5103
+ props.push(`data: import('./$types.js').${isKitLayoutFile ? 'LayoutData' : 'PageData'}`);
4859
5104
  }
4860
- else {
4861
- props.push(`${name}: unknown`);
5105
+ if (name === 'form' && !isKitLayoutFile) {
5106
+ props.push(`form: import('./$types.js').ActionData`);
4862
5107
  }
4863
5108
  }
4864
- }
4865
- if (isKitLayoutFile) {
4866
- props.push(`children: import('svelte').Snippet`);
4867
- }
4868
- if (props.length > 0) {
4869
- propsStr =
4870
- `{ ${props.join(', ')} }` +
4871
- (withUnknown ? ' & Record<string, unknown>' : '');
4872
- }
4873
- else if (withUnknown) {
4874
- propsStr = 'Record<string, unknown>';
4875
- }
4876
- else {
4877
- propsStr = 'Record<string, never>';
5109
+ else if (element.initializer) {
5110
+ const initializer = ts.isCallExpression(element.initializer) &&
5111
+ ts.isIdentifier(element.initializer.expression) &&
5112
+ element.initializer.expression.text === '$bindable'
5113
+ ? element.initializer.arguments[0]
5114
+ : element.initializer;
5115
+ const type = !initializer
5116
+ ? 'any'
5117
+ : ts.isAsExpression(initializer)
5118
+ ? initializer.type.getText()
5119
+ : ts.isStringLiteral(initializer)
5120
+ ? 'string'
5121
+ : ts.isNumericLiteral(initializer)
5122
+ ? 'number'
5123
+ : initializer.kind === ts.SyntaxKind.TrueKeyword ||
5124
+ initializer.kind === ts.SyntaxKind.FalseKeyword
5125
+ ? 'boolean'
5126
+ : ts.isIdentifier(initializer) &&
5127
+ initializer.text !== 'undefined'
5128
+ ? `typeof ${initializer.text}`
5129
+ : ts.isArrowFunction(initializer)
5130
+ ? 'Function'
5131
+ : ts.isObjectLiteralExpression(initializer)
5132
+ ? 'Record<string, any>'
5133
+ : ts.isArrayLiteralExpression(initializer)
5134
+ ? 'any[]'
5135
+ : 'any';
5136
+ props.push(`${name}?: ${type}`);
5137
+ }
5138
+ else {
5139
+ props.push(`${name}: any`);
5140
+ }
4878
5141
  }
4879
5142
  }
4880
- else {
4881
- propsStr = 'Record<string, unknown>';
5143
+ if (isKitLayoutFile) {
5144
+ props.push(`children: import('svelte').Snippet`);
4882
5145
  }
4883
- // Create a virtual type alias for the unnamed generic and reuse it for the props return type
4884
- // so that rename, find references etc works seamlessly across components
4885
- if (this.isTsFile) {
4886
- this.$props.type = '$$ComponentProps';
4887
- if (props.length > 0 || withUnknown) {
4888
- preprendStr(this.str, node.parent.pos + this.astOffset, surroundWithIgnoreComments(`;type $$ComponentProps = ${propsStr};`));
4889
- preprendStr(this.str, node.name.end + this.astOffset, `: ${this.$props.type}`);
4890
- }
5146
+ if (props.length > 0) {
5147
+ propsStr =
5148
+ `{ ${props.join(', ')} }` + (withUnknown ? ' & Record<string, any>' : '');
5149
+ }
5150
+ else if (withUnknown) {
5151
+ propsStr = 'Record<string, any>';
4891
5152
  }
4892
5153
  else {
4893
- this.$props.comment = '/** @type {$$ComponentProps} */';
4894
- if (props.length > 0 || withUnknown) {
4895
- preprendStr(this.str, node.pos + this.astOffset, `/** @typedef {${propsStr}} $$ComponentProps */${this.$props.comment}`);
4896
- }
5154
+ propsStr = 'Record<string, never>';
5155
+ }
5156
+ }
5157
+ else {
5158
+ propsStr = 'Record<string, any>';
5159
+ }
5160
+ // Create a virtual type alias for the unnamed generic and reuse it for the props return type
5161
+ // so that rename, find references etc works seamlessly across components
5162
+ if (this.isTsFile) {
5163
+ this.$props.type = '$$ComponentProps';
5164
+ if (props.length > 0 || withUnknown) {
5165
+ preprendStr(this.str, node.parent.pos + this.astOffset, surroundWithIgnoreComments(`;type $$ComponentProps = ${propsStr};`));
5166
+ preprendStr(this.str, node.name.end + this.astOffset, `: ${this.$props.type}`);
5167
+ }
5168
+ }
5169
+ else {
5170
+ this.$props.comment = '/** @type {$$ComponentProps} */';
5171
+ if (props.length > 0 || withUnknown) {
5172
+ preprendStr(this.str, node.pos + this.astOffset, `/** @typedef {${propsStr}} $$ComponentProps */${this.$props.comment}`);
4897
5173
  }
4898
5174
  }
4899
5175
  }
@@ -6219,7 +6495,7 @@ class InterfacesAndTypes {
6219
6495
  }
6220
6496
  }
6221
6497
 
6222
- function processInstanceScriptContent(str, script, events, implicitStoreValues, mode, hasModuleScript, isTSFile, basename, isSvelte5Plus, isRunes) {
6498
+ function processInstanceScriptContent(str, script, events, implicitStoreValues, mode, moduleAst, isTSFile, basename, isSvelte5Plus, isRunes) {
6223
6499
  const htmlx = str.original;
6224
6500
  const scriptContent = htmlx.substring(script.content.start, script.content.end);
6225
6501
  const tsAst = ts.createSourceFile('component.ts.svelte', scriptContent, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
@@ -6227,6 +6503,9 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6227
6503
  const exportedNames = new ExportedNames(str, astOffset, basename, isTSFile, isSvelte5Plus, isRunes);
6228
6504
  const generics = new Generics(str, astOffset, script);
6229
6505
  const interfacesAndTypes = new InterfacesAndTypes();
6506
+ if (moduleAst) {
6507
+ moduleAst.tsAst.forEachChild((n) => exportedNames.hoistableInterfaces.analyzeModuleScriptNode(n));
6508
+ }
6230
6509
  const implicitTopLevelNames = new ImplicitTopLevelNames(str, astOffset);
6231
6510
  let uses$$props = false;
6232
6511
  let uses$$restProps = false;
@@ -6306,6 +6585,9 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6306
6585
  const walk = (node, parent) => {
6307
6586
  var _a, _b, _c;
6308
6587
  const onLeaveCallbacks = [];
6588
+ if (parent === tsAst) {
6589
+ exportedNames.hoistableInterfaces.analyzeInstanceScriptNode(node);
6590
+ }
6309
6591
  generics.addIfIsGeneric(node);
6310
6592
  if (is$$EventsDeclaration(node)) {
6311
6593
  events.setComponentEventsInterface(node, astOffset);
@@ -6410,7 +6692,7 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6410
6692
  // declare implicit reactive variables we found in the script
6411
6693
  implicitTopLevelNames.modifyCode(rootScope.declared);
6412
6694
  implicitStoreValues.modifyCode(astOffset, str);
6413
- handleFirstInstanceImport(tsAst, astOffset, hasModuleScript, str);
6695
+ handleFirstInstanceImport(tsAst, astOffset, !!moduleAst, str);
6414
6696
  // move interfaces and types out of the render function if they are referenced
6415
6697
  // by a $$Generic, otherwise it will be used before being defined after the transformation
6416
6698
  const nodesToMove = interfacesAndTypes.getNodesWithNames(generics.getTypeReferences());
@@ -6424,6 +6706,7 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6424
6706
  // break dts generation (file will not be generated).
6425
6707
  transformInterfacesToTypes(tsAst, str, astOffset, nodesToMove);
6426
6708
  }
6709
+ exportedNames.hoistableInterfaces.moveHoistableInterfaces(str, astOffset, script.start);
6427
6710
  return {
6428
6711
  exportedNames,
6429
6712
  events,
@@ -6458,11 +6741,15 @@ function transformInterfacesToTypes(tsAst, str, astOffset, movedNodes) {
6458
6741
  });
6459
6742
  }
6460
6743
 
6461
- function processModuleScriptTag(str, script, implicitStoreValues) {
6744
+ function createModuleAst(str, script) {
6462
6745
  const htmlx = str.original;
6463
6746
  const scriptContent = htmlx.substring(script.content.start, script.content.end);
6464
6747
  const tsAst = ts.createSourceFile('component.module.ts.svelte', scriptContent, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
6465
6748
  const astOffset = script.content.start;
6749
+ return { htmlx, tsAst, astOffset };
6750
+ }
6751
+ function processModuleScriptTag(str, script, implicitStoreValues, moduleAst) {
6752
+ const { htmlx, tsAst, astOffset } = moduleAst;
6466
6753
  const generics = new Generics(str, astOffset, script);
6467
6754
  if (generics.genericsAttr) {
6468
6755
  const start = htmlx.indexOf('generics', script.start);
@@ -7095,7 +7382,9 @@ function svelte2tsx(svelte, options = { parse: compiler.parse }) {
7095
7382
  * In this case we instead have to move it to moduleScriptTag.end. We track the location for the script move in the MoveInstanceScriptTarget var
7096
7383
  */
7097
7384
  let instanceScriptTarget = 0;
7385
+ let moduleAst;
7098
7386
  if (moduleScriptTag) {
7387
+ moduleAst = createModuleAst(str, moduleScriptTag);
7099
7388
  if (moduleScriptTag.start != 0) {
7100
7389
  //move our module tag to the top
7101
7390
  str.move(moduleScriptTag.start, moduleScriptTag.end, 0);
@@ -7118,8 +7407,7 @@ function svelte2tsx(svelte, options = { parse: compiler.parse }) {
7118
7407
  if (scriptTag.start != instanceScriptTarget) {
7119
7408
  str.move(scriptTag.start, scriptTag.end, instanceScriptTarget);
7120
7409
  }
7121
- const res = processInstanceScriptContent(str, scriptTag, events, implicitStoreValues, options.mode,
7122
- /**hasModuleScripts */ !!moduleScriptTag, options === null || options === void 0 ? void 0 : options.isTsFile, basename, svelte5Plus, isRunes);
7410
+ const res = processInstanceScriptContent(str, scriptTag, events, implicitStoreValues, options.mode, moduleAst, options === null || options === void 0 ? void 0 : options.isTsFile, basename, svelte5Plus, isRunes);
7123
7411
  uses$$props = uses$$props || res.uses$$props;
7124
7412
  uses$$restProps = uses$$restProps || res.uses$$restProps;
7125
7413
  uses$$slots = uses$$slots || res.uses$$slots;
@@ -7148,7 +7436,7 @@ function svelte2tsx(svelte, options = { parse: compiler.parse }) {
7148
7436
  });
7149
7437
  // we need to process the module script after the instance script has moved otherwise we get warnings about moving edited items
7150
7438
  if (moduleScriptTag) {
7151
- processModuleScriptTag(str, moduleScriptTag, new ImplicitStoreValues(implicitStoreValues.getAccessedStores(), renderFunctionStart, scriptTag || options.mode === 'ts' ? undefined : (input) => `</>;${input}<>`));
7439
+ processModuleScriptTag(str, moduleScriptTag, new ImplicitStoreValues(implicitStoreValues.getAccessedStores(), renderFunctionStart, scriptTag || options.mode === 'ts' ? undefined : (input) => `</>;${input}<>`), moduleAst);
7152
7440
  }
7153
7441
  addComponentExport({
7154
7442
  str,