svelte2tsx 0.5.21 → 0.5.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.
package/index.js CHANGED
@@ -4932,6 +4932,49 @@ function getNamesFromLabeledStatement(node) {
4932
4932
  // svelte won't let you create a variable with $ prefix (reserved for stores)
4933
4933
  .filter((name) => !name.startsWith('$')));
4934
4934
  }
4935
+ /**
4936
+ * move node to top of script so they appear outside our render function
4937
+ */
4938
+ function moveNode(node, str, astOffset, scriptStart, sourceFile) {
4939
+ var _a;
4940
+ const scanner = ts__default['default'].createScanner(sourceFile.languageVersion,
4941
+ /*skipTrivia*/ false, sourceFile.languageVariant);
4942
+ const comments = (_a = ts__default['default'].getLeadingCommentRanges(node.getFullText(), 0)) !== null && _a !== void 0 ? _a : [];
4943
+ if (!comments.some((comment) => comment.hasTrailingNewLine) &&
4944
+ isNewGroup(sourceFile, node, scanner)) {
4945
+ str.appendRight(node.getStart() + astOffset, '\n');
4946
+ }
4947
+ for (const comment of comments) {
4948
+ const commentEnd = node.pos + comment.end + astOffset;
4949
+ str.move(node.pos + comment.pos + astOffset, commentEnd, scriptStart + 1);
4950
+ if (comment.hasTrailingNewLine) {
4951
+ str.overwrite(commentEnd - 1, commentEnd, str.original[commentEnd - 1] + '\n');
4952
+ }
4953
+ }
4954
+ str.move(node.getStart() + astOffset, node.end + astOffset, scriptStart + 1);
4955
+ //add in a \n
4956
+ const originalEndChar = str.original[node.end + astOffset - 1];
4957
+ str.overwrite(node.end + astOffset - 1, node.end + astOffset, originalEndChar + '\n');
4958
+ }
4959
+ /**
4960
+ * adopted from https://github.com/microsoft/TypeScript/blob/6e0447fdf165b1cec9fc80802abcc15bd23a268f/src/services/organizeImports.ts#L111
4961
+ */
4962
+ function isNewGroup(sourceFile, topLevelImportDecl, scanner) {
4963
+ const startPos = topLevelImportDecl.getFullStart();
4964
+ const endPos = topLevelImportDecl.getStart();
4965
+ scanner.setText(sourceFile.text, startPos, endPos - startPos);
4966
+ let numberOfNewLines = 0;
4967
+ while (scanner.getTokenPos() < endPos) {
4968
+ const tokenKind = scanner.scan();
4969
+ if (tokenKind === ts__default['default'].SyntaxKind.NewLineTrivia) {
4970
+ numberOfNewLines++;
4971
+ if (numberOfNewLines >= 2) {
4972
+ return true;
4973
+ }
4974
+ }
4975
+ }
4976
+ return false;
4977
+ }
4935
4978
 
4936
4979
  function is$$EventsDeclaration(node) {
4937
4980
  return isInterfaceOrTypeDeclaration(node) && node.name.text === '$$Events';
@@ -5353,6 +5396,9 @@ class ExportedNames {
5353
5396
  constructor(str, astOffset) {
5354
5397
  this.str = str;
5355
5398
  this.astOffset = astOffset;
5399
+ /**
5400
+ * Uses the $$Props type
5401
+ */
5356
5402
  this.uses$$Props = false;
5357
5403
  this.exports = new Map();
5358
5404
  this.possibleExports = new Map();
@@ -5488,7 +5534,11 @@ class ExportedNames {
5488
5534
  }
5489
5535
  createClassGetters() {
5490
5536
  return Array.from(this.getters)
5491
- .map((name) => `\n get ${name}() { return this.$$prop_def.${name} }`)
5537
+ .map((name) =>
5538
+ // getters are const/classes/functions, which are always defined.
5539
+ // We have to remove the `| undefined` from the type here because it was necessary to
5540
+ // be added in a previous step so people are not expected to provide these as props.
5541
+ `\n get ${name}() { return __sveltets_2_nonNullable(this.$$prop_def.${name}) }`)
5492
5542
  .join('');
5493
5543
  }
5494
5544
  createClassAccessors() {
@@ -5581,8 +5631,9 @@ class ExportedNames {
5581
5631
  * Creates a string from the collected props
5582
5632
  *
5583
5633
  * @param isTsFile Whether this is a TypeScript file or not.
5634
+ * @param uses$$propsValue whether the file references the $$props variable
5584
5635
  */
5585
- createPropsStr(isTsFile) {
5636
+ createPropsStr(isTsFile, uses$$propsValue) {
5586
5637
  const names = Array.from(this.exports.entries());
5587
5638
  if (this.uses$$Props) {
5588
5639
  const lets = names.filter(([, { isLet }]) => isLet);
@@ -5608,7 +5659,7 @@ class ExportedNames {
5608
5659
  this.createReturnElementsType(others).join(',') +
5609
5660
  '}}');
5610
5661
  }
5611
- if (names.length === 0) {
5662
+ if (names.length === 0 && !uses$$propsValue) {
5612
5663
  // Necessary, because {} roughly equals to any
5613
5664
  return isTsFile
5614
5665
  ? '{} as Record<string, never>'
@@ -6332,6 +6383,7 @@ class Generics {
6332
6383
  this.str = str;
6333
6384
  this.astOffset = astOffset;
6334
6385
  this.definitions = [];
6386
+ this.typeReferences = [];
6335
6387
  this.references = [];
6336
6388
  }
6337
6389
  addIfIsGeneric(node) {
@@ -6341,10 +6393,12 @@ class Generics {
6341
6393
  throw new Error('Invalid $$Generic declaration: Only one type argument allowed');
6342
6394
  }
6343
6395
  if (((_b = node.type.typeArguments) === null || _b === void 0 ? void 0 : _b.length) === 1) {
6344
- this.definitions.push(`${node.name.text} extends ${node.type.typeArguments[0].getText()}`);
6396
+ const typeReference = node.type.typeArguments[0].getText();
6397
+ this.typeReferences.push(typeReference);
6398
+ this.definitions.push(`${node.name.text} extends ${typeReference}`);
6345
6399
  }
6346
6400
  else {
6347
- this.definitions.push(`${node.name.text}`);
6401
+ this.definitions.push(node.name.text);
6348
6402
  }
6349
6403
  this.references.push(node.name.text);
6350
6404
  this.str.remove(this.astOffset + node.getStart(), this.astOffset + node.getEnd());
@@ -6360,6 +6414,9 @@ class Generics {
6360
6414
  ts__default['default'].isIdentifier(node.typeName) &&
6361
6415
  node.typeName.text === '$$Generic');
6362
6416
  }
6417
+ getTypeReferences() {
6418
+ return this.typeReferences;
6419
+ }
6363
6420
  toDefinitionString(addIgnore = false) {
6364
6421
  const surround = addIgnore ? surroundWithIgnoreComments : (str) => str;
6365
6422
  return this.definitions.length ? surround(`<${this.definitions.join(',')}>`) : '';
@@ -6376,44 +6433,7 @@ class Generics {
6376
6433
  * move imports to top of script so they appear outside our render function
6377
6434
  */
6378
6435
  function handleImportDeclaration(node, str, astOffset, scriptStart, sourceFile) {
6379
- var _a;
6380
- const scanner = ts__default['default'].createScanner(sourceFile.languageVersion,
6381
- /*skipTrivia*/ false, sourceFile.languageVariant);
6382
- const comments = (_a = ts__default['default'].getLeadingCommentRanges(node.getFullText(), 0)) !== null && _a !== void 0 ? _a : [];
6383
- if (!comments.some((comment) => comment.hasTrailingNewLine) &&
6384
- isNewGroup(sourceFile, node, scanner)) {
6385
- str.appendRight(node.getStart() + astOffset, '\n');
6386
- }
6387
- for (const comment of comments) {
6388
- const commentEnd = node.pos + comment.end + astOffset;
6389
- str.move(node.pos + comment.pos + astOffset, commentEnd, scriptStart + 1);
6390
- if (comment.hasTrailingNewLine) {
6391
- str.overwrite(commentEnd - 1, commentEnd, str.original[commentEnd - 1] + '\n');
6392
- }
6393
- }
6394
- str.move(node.getStart() + astOffset, node.end + astOffset, scriptStart + 1);
6395
- //add in a \n
6396
- const originalEndChar = str.original[node.end + astOffset - 1];
6397
- str.overwrite(node.end + astOffset - 1, node.end + astOffset, originalEndChar + '\n');
6398
- }
6399
- /**
6400
- * adopted from https://github.com/microsoft/TypeScript/blob/6e0447fdf165b1cec9fc80802abcc15bd23a268f/src/services/organizeImports.ts#L111
6401
- */
6402
- function isNewGroup(sourceFile, topLevelImportDecl, scanner) {
6403
- const startPos = topLevelImportDecl.getFullStart();
6404
- const endPos = topLevelImportDecl.getStart();
6405
- scanner.setText(sourceFile.text, startPos, endPos - startPos);
6406
- let numberOfNewLines = 0;
6407
- while (scanner.getTokenPos() < endPos) {
6408
- const tokenKind = scanner.scan();
6409
- if (tokenKind === ts__default['default'].SyntaxKind.NewLineTrivia) {
6410
- numberOfNewLines++;
6411
- if (numberOfNewLines >= 2) {
6412
- return true;
6413
- }
6414
- }
6415
- }
6416
- return false;
6436
+ return moveNode(node, str, astOffset, scriptStart, sourceFile);
6417
6437
  }
6418
6438
  /**
6419
6439
  * ensure it's in a newline.
@@ -6434,6 +6454,56 @@ function handleFirstInstanceImport(tsAst, astOffset, hasModuleScript, str) {
6434
6454
  str.appendRight(start + astOffset, '\n' + (hasModuleScript ? '\n' : ''));
6435
6455
  }
6436
6456
 
6457
+ function flatten(arr) {
6458
+ return arr.reduce((acc, val) => acc.concat(val), []);
6459
+ }
6460
+
6461
+ class InterfacesAndTypes {
6462
+ constructor() {
6463
+ this.node = null;
6464
+ this.all = [];
6465
+ this.references = new Map();
6466
+ }
6467
+ add(node) {
6468
+ this.all.push(node);
6469
+ }
6470
+ getNodesWithNames(names) {
6471
+ return this.all.filter((node) => names.includes(node.name.text));
6472
+ }
6473
+ // The following could be used to create a informative error message in case
6474
+ // someone has an interface that both references a generic and is used by one:
6475
+ addReference(reference) {
6476
+ if (!this.node) {
6477
+ return;
6478
+ }
6479
+ const references = this.references.get(this.node) || [];
6480
+ references.push(reference);
6481
+ this.references.set(this.node, references);
6482
+ }
6483
+ getNodesThatReferenceType(name) {
6484
+ const nodes = [];
6485
+ for (const [node, references] of this.references) {
6486
+ if (references.some((r) => r.typeName.getText() === name)) {
6487
+ nodes.push(node);
6488
+ }
6489
+ }
6490
+ return nodes;
6491
+ }
6492
+ getNodesThatRecursivelyReferenceType(name) {
6493
+ let types = [name];
6494
+ const nodes = new Set();
6495
+ while (types.length !== 0) {
6496
+ const newTypes = flatten(types.map((type) => this.getNodesThatReferenceType(type))).filter((t) => !nodes.has(t));
6497
+ newTypes.forEach((t) => nodes.add(t));
6498
+ types = newTypes.map((t) => t.name.text);
6499
+ }
6500
+ return [...nodes.values()];
6501
+ }
6502
+ getNodesThatRecursivelyReferenceTypes(names) {
6503
+ return flatten(names.map((name) => this.getNodesThatRecursivelyReferenceType(name)));
6504
+ }
6505
+ }
6506
+
6437
6507
  function processInstanceScriptContent(str, script, events, implicitStoreValues, mode, hasModuleScript) {
6438
6508
  const htmlx = str.original;
6439
6509
  const scriptContent = htmlx.substring(script.content.start, script.content.end);
@@ -6441,6 +6511,7 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6441
6511
  const astOffset = script.content.start;
6442
6512
  const exportedNames = new ExportedNames(str, astOffset);
6443
6513
  const generics = new Generics(str, astOffset);
6514
+ const interfacesAndTypes = new InterfacesAndTypes();
6444
6515
  const implicitTopLevelNames = new ImplicitTopLevelNames(str, astOffset);
6445
6516
  let uses$$props = false;
6446
6517
  let uses$$restProps = false;
@@ -6576,6 +6647,11 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6576
6647
  if (ts__namespace.isImportSpecifier(node)) {
6577
6648
  implicitStoreValues.addImportStatement(node);
6578
6649
  }
6650
+ if (ts__namespace.isTypeAliasDeclaration(node) || ts__namespace.isInterfaceDeclaration(node)) {
6651
+ interfacesAndTypes.node = node;
6652
+ interfacesAndTypes.add(node);
6653
+ onLeaveCallbacks.push(() => (interfacesAndTypes.node = null));
6654
+ }
6579
6655
  //handle stores etc
6580
6656
  if (ts__namespace.isIdentifier(node)) {
6581
6657
  handleIdentifier(node, parent);
@@ -6611,12 +6687,18 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6611
6687
  implicitTopLevelNames.modifyCode(rootScope.declared);
6612
6688
  implicitStoreValues.modifyCode(astOffset, str);
6613
6689
  handleFirstInstanceImport(tsAst, astOffset, hasModuleScript, str);
6690
+ // move interfaces and types out of the render function if they are referenced
6691
+ // by a $$Generic, otherwise it will be used before being defined after the transformation
6692
+ const nodesToMove = interfacesAndTypes.getNodesWithNames(generics.getTypeReferences());
6693
+ for (const node of nodesToMove) {
6694
+ moveNode(node, str, astOffset, script.start, tsAst);
6695
+ }
6614
6696
  if (mode === 'dts') {
6615
6697
  // Transform interface declarations to type declarations because indirectly
6616
6698
  // using interfaces inside the return type of a function is forbidden.
6617
6699
  // This is not a problem for intellisense/type inference but it will
6618
6700
  // break dts generation (file will not be generated).
6619
- transformInterfacesToTypes(tsAst, str, astOffset);
6701
+ transformInterfacesToTypes(tsAst, str, astOffset, nodesToMove);
6620
6702
  }
6621
6703
  return {
6622
6704
  exportedNames,
@@ -6628,8 +6710,11 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6628
6710
  generics
6629
6711
  };
6630
6712
  }
6631
- function transformInterfacesToTypes(tsAst, str, astOffset) {
6632
- tsAst.statements.filter(ts__namespace.isInterfaceDeclaration).forEach((node) => {
6713
+ function transformInterfacesToTypes(tsAst, str, astOffset, movedNodes) {
6714
+ tsAst.statements
6715
+ .filter(ts__namespace.isInterfaceDeclaration)
6716
+ .filter((i) => !movedNodes.includes(i))
6717
+ .forEach((node) => {
6633
6718
  var _a;
6634
6719
  str.overwrite(node.getStart() + astOffset, node.getStart() + astOffset + 'interface'.length, 'type');
6635
6720
  if ((_a = node.heritageClauses) === null || _a === void 0 ? void 0 : _a.length) {
@@ -6904,7 +6989,7 @@ function createRenderFunction({ str, scriptTag, scriptDestination, slots, events
6904
6989
  })
6905
6990
  .join(', ') +
6906
6991
  '}';
6907
- const returnString = `\nreturn { props: ${exportedNames.createPropsStr(isTsFile)}` +
6992
+ const returnString = `\nreturn { props: ${exportedNames.createPropsStr(isTsFile, uses$$props)}` +
6908
6993
  `, slots: ${slotsAsDef}` +
6909
6994
  `, events: ${events.toDefString()} }}`;
6910
6995
  // wrap template with callback
package/index.mjs CHANGED
@@ -4902,6 +4902,49 @@ function getNamesFromLabeledStatement(node) {
4902
4902
  // svelte won't let you create a variable with $ prefix (reserved for stores)
4903
4903
  .filter((name) => !name.startsWith('$')));
4904
4904
  }
4905
+ /**
4906
+ * move node to top of script so they appear outside our render function
4907
+ */
4908
+ function moveNode(node, str, astOffset, scriptStart, sourceFile) {
4909
+ var _a;
4910
+ const scanner = ts__default.createScanner(sourceFile.languageVersion,
4911
+ /*skipTrivia*/ false, sourceFile.languageVariant);
4912
+ const comments = (_a = ts__default.getLeadingCommentRanges(node.getFullText(), 0)) !== null && _a !== void 0 ? _a : [];
4913
+ if (!comments.some((comment) => comment.hasTrailingNewLine) &&
4914
+ isNewGroup(sourceFile, node, scanner)) {
4915
+ str.appendRight(node.getStart() + astOffset, '\n');
4916
+ }
4917
+ for (const comment of comments) {
4918
+ const commentEnd = node.pos + comment.end + astOffset;
4919
+ str.move(node.pos + comment.pos + astOffset, commentEnd, scriptStart + 1);
4920
+ if (comment.hasTrailingNewLine) {
4921
+ str.overwrite(commentEnd - 1, commentEnd, str.original[commentEnd - 1] + '\n');
4922
+ }
4923
+ }
4924
+ str.move(node.getStart() + astOffset, node.end + astOffset, scriptStart + 1);
4925
+ //add in a \n
4926
+ const originalEndChar = str.original[node.end + astOffset - 1];
4927
+ str.overwrite(node.end + astOffset - 1, node.end + astOffset, originalEndChar + '\n');
4928
+ }
4929
+ /**
4930
+ * adopted from https://github.com/microsoft/TypeScript/blob/6e0447fdf165b1cec9fc80802abcc15bd23a268f/src/services/organizeImports.ts#L111
4931
+ */
4932
+ function isNewGroup(sourceFile, topLevelImportDecl, scanner) {
4933
+ const startPos = topLevelImportDecl.getFullStart();
4934
+ const endPos = topLevelImportDecl.getStart();
4935
+ scanner.setText(sourceFile.text, startPos, endPos - startPos);
4936
+ let numberOfNewLines = 0;
4937
+ while (scanner.getTokenPos() < endPos) {
4938
+ const tokenKind = scanner.scan();
4939
+ if (tokenKind === ts__default.SyntaxKind.NewLineTrivia) {
4940
+ numberOfNewLines++;
4941
+ if (numberOfNewLines >= 2) {
4942
+ return true;
4943
+ }
4944
+ }
4945
+ }
4946
+ return false;
4947
+ }
4905
4948
 
4906
4949
  function is$$EventsDeclaration(node) {
4907
4950
  return isInterfaceOrTypeDeclaration(node) && node.name.text === '$$Events';
@@ -5323,6 +5366,9 @@ class ExportedNames {
5323
5366
  constructor(str, astOffset) {
5324
5367
  this.str = str;
5325
5368
  this.astOffset = astOffset;
5369
+ /**
5370
+ * Uses the $$Props type
5371
+ */
5326
5372
  this.uses$$Props = false;
5327
5373
  this.exports = new Map();
5328
5374
  this.possibleExports = new Map();
@@ -5458,7 +5504,11 @@ class ExportedNames {
5458
5504
  }
5459
5505
  createClassGetters() {
5460
5506
  return Array.from(this.getters)
5461
- .map((name) => `\n get ${name}() { return this.$$prop_def.${name} }`)
5507
+ .map((name) =>
5508
+ // getters are const/classes/functions, which are always defined.
5509
+ // We have to remove the `| undefined` from the type here because it was necessary to
5510
+ // be added in a previous step so people are not expected to provide these as props.
5511
+ `\n get ${name}() { return __sveltets_2_nonNullable(this.$$prop_def.${name}) }`)
5462
5512
  .join('');
5463
5513
  }
5464
5514
  createClassAccessors() {
@@ -5551,8 +5601,9 @@ class ExportedNames {
5551
5601
  * Creates a string from the collected props
5552
5602
  *
5553
5603
  * @param isTsFile Whether this is a TypeScript file or not.
5604
+ * @param uses$$propsValue whether the file references the $$props variable
5554
5605
  */
5555
- createPropsStr(isTsFile) {
5606
+ createPropsStr(isTsFile, uses$$propsValue) {
5556
5607
  const names = Array.from(this.exports.entries());
5557
5608
  if (this.uses$$Props) {
5558
5609
  const lets = names.filter(([, { isLet }]) => isLet);
@@ -5578,7 +5629,7 @@ class ExportedNames {
5578
5629
  this.createReturnElementsType(others).join(',') +
5579
5630
  '}}');
5580
5631
  }
5581
- if (names.length === 0) {
5632
+ if (names.length === 0 && !uses$$propsValue) {
5582
5633
  // Necessary, because {} roughly equals to any
5583
5634
  return isTsFile
5584
5635
  ? '{} as Record<string, never>'
@@ -6302,6 +6353,7 @@ class Generics {
6302
6353
  this.str = str;
6303
6354
  this.astOffset = astOffset;
6304
6355
  this.definitions = [];
6356
+ this.typeReferences = [];
6305
6357
  this.references = [];
6306
6358
  }
6307
6359
  addIfIsGeneric(node) {
@@ -6311,10 +6363,12 @@ class Generics {
6311
6363
  throw new Error('Invalid $$Generic declaration: Only one type argument allowed');
6312
6364
  }
6313
6365
  if (((_b = node.type.typeArguments) === null || _b === void 0 ? void 0 : _b.length) === 1) {
6314
- this.definitions.push(`${node.name.text} extends ${node.type.typeArguments[0].getText()}`);
6366
+ const typeReference = node.type.typeArguments[0].getText();
6367
+ this.typeReferences.push(typeReference);
6368
+ this.definitions.push(`${node.name.text} extends ${typeReference}`);
6315
6369
  }
6316
6370
  else {
6317
- this.definitions.push(`${node.name.text}`);
6371
+ this.definitions.push(node.name.text);
6318
6372
  }
6319
6373
  this.references.push(node.name.text);
6320
6374
  this.str.remove(this.astOffset + node.getStart(), this.astOffset + node.getEnd());
@@ -6330,6 +6384,9 @@ class Generics {
6330
6384
  ts__default.isIdentifier(node.typeName) &&
6331
6385
  node.typeName.text === '$$Generic');
6332
6386
  }
6387
+ getTypeReferences() {
6388
+ return this.typeReferences;
6389
+ }
6333
6390
  toDefinitionString(addIgnore = false) {
6334
6391
  const surround = addIgnore ? surroundWithIgnoreComments : (str) => str;
6335
6392
  return this.definitions.length ? surround(`<${this.definitions.join(',')}>`) : '';
@@ -6346,44 +6403,7 @@ class Generics {
6346
6403
  * move imports to top of script so they appear outside our render function
6347
6404
  */
6348
6405
  function handleImportDeclaration(node, str, astOffset, scriptStart, sourceFile) {
6349
- var _a;
6350
- const scanner = ts__default.createScanner(sourceFile.languageVersion,
6351
- /*skipTrivia*/ false, sourceFile.languageVariant);
6352
- const comments = (_a = ts__default.getLeadingCommentRanges(node.getFullText(), 0)) !== null && _a !== void 0 ? _a : [];
6353
- if (!comments.some((comment) => comment.hasTrailingNewLine) &&
6354
- isNewGroup(sourceFile, node, scanner)) {
6355
- str.appendRight(node.getStart() + astOffset, '\n');
6356
- }
6357
- for (const comment of comments) {
6358
- const commentEnd = node.pos + comment.end + astOffset;
6359
- str.move(node.pos + comment.pos + astOffset, commentEnd, scriptStart + 1);
6360
- if (comment.hasTrailingNewLine) {
6361
- str.overwrite(commentEnd - 1, commentEnd, str.original[commentEnd - 1] + '\n');
6362
- }
6363
- }
6364
- str.move(node.getStart() + astOffset, node.end + astOffset, scriptStart + 1);
6365
- //add in a \n
6366
- const originalEndChar = str.original[node.end + astOffset - 1];
6367
- str.overwrite(node.end + astOffset - 1, node.end + astOffset, originalEndChar + '\n');
6368
- }
6369
- /**
6370
- * adopted from https://github.com/microsoft/TypeScript/blob/6e0447fdf165b1cec9fc80802abcc15bd23a268f/src/services/organizeImports.ts#L111
6371
- */
6372
- function isNewGroup(sourceFile, topLevelImportDecl, scanner) {
6373
- const startPos = topLevelImportDecl.getFullStart();
6374
- const endPos = topLevelImportDecl.getStart();
6375
- scanner.setText(sourceFile.text, startPos, endPos - startPos);
6376
- let numberOfNewLines = 0;
6377
- while (scanner.getTokenPos() < endPos) {
6378
- const tokenKind = scanner.scan();
6379
- if (tokenKind === ts__default.SyntaxKind.NewLineTrivia) {
6380
- numberOfNewLines++;
6381
- if (numberOfNewLines >= 2) {
6382
- return true;
6383
- }
6384
- }
6385
- }
6386
- return false;
6406
+ return moveNode(node, str, astOffset, scriptStart, sourceFile);
6387
6407
  }
6388
6408
  /**
6389
6409
  * ensure it's in a newline.
@@ -6404,6 +6424,56 @@ function handleFirstInstanceImport(tsAst, astOffset, hasModuleScript, str) {
6404
6424
  str.appendRight(start + astOffset, '\n' + (hasModuleScript ? '\n' : ''));
6405
6425
  }
6406
6426
 
6427
+ function flatten(arr) {
6428
+ return arr.reduce((acc, val) => acc.concat(val), []);
6429
+ }
6430
+
6431
+ class InterfacesAndTypes {
6432
+ constructor() {
6433
+ this.node = null;
6434
+ this.all = [];
6435
+ this.references = new Map();
6436
+ }
6437
+ add(node) {
6438
+ this.all.push(node);
6439
+ }
6440
+ getNodesWithNames(names) {
6441
+ return this.all.filter((node) => names.includes(node.name.text));
6442
+ }
6443
+ // The following could be used to create a informative error message in case
6444
+ // someone has an interface that both references a generic and is used by one:
6445
+ addReference(reference) {
6446
+ if (!this.node) {
6447
+ return;
6448
+ }
6449
+ const references = this.references.get(this.node) || [];
6450
+ references.push(reference);
6451
+ this.references.set(this.node, references);
6452
+ }
6453
+ getNodesThatReferenceType(name) {
6454
+ const nodes = [];
6455
+ for (const [node, references] of this.references) {
6456
+ if (references.some((r) => r.typeName.getText() === name)) {
6457
+ nodes.push(node);
6458
+ }
6459
+ }
6460
+ return nodes;
6461
+ }
6462
+ getNodesThatRecursivelyReferenceType(name) {
6463
+ let types = [name];
6464
+ const nodes = new Set();
6465
+ while (types.length !== 0) {
6466
+ const newTypes = flatten(types.map((type) => this.getNodesThatReferenceType(type))).filter((t) => !nodes.has(t));
6467
+ newTypes.forEach((t) => nodes.add(t));
6468
+ types = newTypes.map((t) => t.name.text);
6469
+ }
6470
+ return [...nodes.values()];
6471
+ }
6472
+ getNodesThatRecursivelyReferenceTypes(names) {
6473
+ return flatten(names.map((name) => this.getNodesThatRecursivelyReferenceType(name)));
6474
+ }
6475
+ }
6476
+
6407
6477
  function processInstanceScriptContent(str, script, events, implicitStoreValues, mode, hasModuleScript) {
6408
6478
  const htmlx = str.original;
6409
6479
  const scriptContent = htmlx.substring(script.content.start, script.content.end);
@@ -6411,6 +6481,7 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6411
6481
  const astOffset = script.content.start;
6412
6482
  const exportedNames = new ExportedNames(str, astOffset);
6413
6483
  const generics = new Generics(str, astOffset);
6484
+ const interfacesAndTypes = new InterfacesAndTypes();
6414
6485
  const implicitTopLevelNames = new ImplicitTopLevelNames(str, astOffset);
6415
6486
  let uses$$props = false;
6416
6487
  let uses$$restProps = false;
@@ -6546,6 +6617,11 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6546
6617
  if (ts.isImportSpecifier(node)) {
6547
6618
  implicitStoreValues.addImportStatement(node);
6548
6619
  }
6620
+ if (ts.isTypeAliasDeclaration(node) || ts.isInterfaceDeclaration(node)) {
6621
+ interfacesAndTypes.node = node;
6622
+ interfacesAndTypes.add(node);
6623
+ onLeaveCallbacks.push(() => (interfacesAndTypes.node = null));
6624
+ }
6549
6625
  //handle stores etc
6550
6626
  if (ts.isIdentifier(node)) {
6551
6627
  handleIdentifier(node, parent);
@@ -6581,12 +6657,18 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6581
6657
  implicitTopLevelNames.modifyCode(rootScope.declared);
6582
6658
  implicitStoreValues.modifyCode(astOffset, str);
6583
6659
  handleFirstInstanceImport(tsAst, astOffset, hasModuleScript, str);
6660
+ // move interfaces and types out of the render function if they are referenced
6661
+ // by a $$Generic, otherwise it will be used before being defined after the transformation
6662
+ const nodesToMove = interfacesAndTypes.getNodesWithNames(generics.getTypeReferences());
6663
+ for (const node of nodesToMove) {
6664
+ moveNode(node, str, astOffset, script.start, tsAst);
6665
+ }
6584
6666
  if (mode === 'dts') {
6585
6667
  // Transform interface declarations to type declarations because indirectly
6586
6668
  // using interfaces inside the return type of a function is forbidden.
6587
6669
  // This is not a problem for intellisense/type inference but it will
6588
6670
  // break dts generation (file will not be generated).
6589
- transformInterfacesToTypes(tsAst, str, astOffset);
6671
+ transformInterfacesToTypes(tsAst, str, astOffset, nodesToMove);
6590
6672
  }
6591
6673
  return {
6592
6674
  exportedNames,
@@ -6598,8 +6680,11 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6598
6680
  generics
6599
6681
  };
6600
6682
  }
6601
- function transformInterfacesToTypes(tsAst, str, astOffset) {
6602
- tsAst.statements.filter(ts.isInterfaceDeclaration).forEach((node) => {
6683
+ function transformInterfacesToTypes(tsAst, str, astOffset, movedNodes) {
6684
+ tsAst.statements
6685
+ .filter(ts.isInterfaceDeclaration)
6686
+ .filter((i) => !movedNodes.includes(i))
6687
+ .forEach((node) => {
6603
6688
  var _a;
6604
6689
  str.overwrite(node.getStart() + astOffset, node.getStart() + astOffset + 'interface'.length, 'type');
6605
6690
  if ((_a = node.heritageClauses) === null || _a === void 0 ? void 0 : _a.length) {
@@ -6874,7 +6959,7 @@ function createRenderFunction({ str, scriptTag, scriptDestination, slots, events
6874
6959
  })
6875
6960
  .join(', ') +
6876
6961
  '}';
6877
- const returnString = `\nreturn { props: ${exportedNames.createPropsStr(isTsFile)}` +
6962
+ const returnString = `\nreturn { props: ${exportedNames.createPropsStr(isTsFile, uses$$props)}` +
6878
6963
  `, slots: ${slotsAsDef}` +
6879
6964
  `, events: ${events.toDefString()} }}`;
6880
6965
  // wrap template with callback
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte2tsx",
3
- "version": "0.5.21",
3
+ "version": "0.5.23",
4
4
  "description": "Convert Svelte components to TSX for type checking",
5
5
  "author": "David Pershouse",
6
6
  "license": "MIT",
@@ -87,7 +87,7 @@ export interface DOMAttributes<T extends EventTarget> {
87
87
  'on:reset'?: FormEventHandler<T> | undefined | null;
88
88
  'on:submit'?: EventHandler<Event, T> | undefined | null; // TODO make this SubmitEvent once we require TS>=4.4
89
89
  'on:invalid'?: EventHandler<Event, T> | undefined | null;
90
-
90
+ 'on:formdata'?: EventHandler<Event & { readonly formData: FormData; }, T> | undefined | null; // TODO make this FormDataEvent once we require TS>=4.4
91
91
 
92
92
  // Image Events
93
93
  'on:load'?: EventHandler | undefined | null;
@@ -538,6 +538,12 @@ export interface HTMLAttributes<T extends EventTarget> extends AriaAttributes, D
538
538
  * Elements with the contenteditable attribute support innerHTML and textContent bindings.
539
539
  */
540
540
  'bind:textContent'?: string | undefined | null;
541
+
542
+ // SvelteKit
543
+ 'data-sveltekit-noscroll'?: true | '' | 'off' | undefined | null;
544
+ 'data-sveltekit-preload-code'?: true | '' | 'eager' | 'viewport' | 'hover' | 'tap' | 'off' | undefined | null;
545
+ 'data-sveltekit-preload-data'?: true | '' | 'hover' | 'tap' | 'off' | undefined | null;
546
+ 'data-sveltekit-reload'?: true | '' | 'off' | undefined | null;
541
547
  }
542
548
 
543
549
  export type HTMLAttributeAnchorTarget =
@@ -558,12 +564,6 @@ export interface HTMLAnchorAttributes extends HTMLAttributes<HTMLAnchorElement>
558
564
  type?: string | undefined | null;
559
565
  referrerpolicy?: ReferrerPolicy | undefined | null;
560
566
 
561
- // SvelteKit
562
- 'data-sveltekit-noscroll'?: true | undefined | null;
563
- 'data-sveltekit-preload-code'?: true | 'eager' | 'viewport' | 'hover' | 'tap' | 'off' | undefined | null;
564
- 'data-sveltekit-preload-data'?: true | 'hover' | 'tap' | 'off' | undefined | null;
565
- 'data-sveltekit-reload'?: true | undefined | null;
566
-
567
567
  // Sapper
568
568
  'sapper:noscroll'?: true | undefined | null;
569
569
  'sapper:prefetch'?: true | undefined | null;
package/svelte-jsx.d.ts CHANGED
@@ -348,6 +348,7 @@ declare namespace svelte.JSX {
348
348
  onsubmit?: EventHandler<SubmitEvent, T> | undefined | null;
349
349
  oninvalid?: EventHandler<Event, T> | undefined | null;
350
350
  onbeforeinput?: EventHandler<InputEvent, T> | undefined | null;
351
+ 'on:formdata'?: EventHandler<FormDataEvent, T> | undefined | null;
351
352
 
352
353
  // Image Events
353
354
  onload?: EventHandler | undefined | null;
@@ -838,6 +839,11 @@ declare namespace svelte.JSX {
838
839
  results?: number | undefined | null;
839
840
  security?: string | undefined | null;
840
841
  unselectable?: boolean | undefined | null;
842
+
843
+ 'data-sveltekit-noscroll'?: true | '' | 'off' | undefined | null;
844
+ 'data-sveltekit-preload-code'?: true | '' | 'eager' | 'viewport' | 'hover' | 'tap' | 'off' | undefined | null;
845
+ 'data-sveltekit-preload-data'?: true | '' | 'hover' | 'tap' | 'off' | undefined | null;
846
+ 'data-sveltekit-reload'?: true | '' | 'off' | undefined | null;
841
847
  }
842
848
 
843
849
  // this list is "complete" in that it contains every SVG attribute
@@ -1175,13 +1181,6 @@ declare namespace svelte.JSX {
1175
1181
  sapperPrefetch?: true | undefined | null;
1176
1182
  }
1177
1183
 
1178
- interface SvelteKitAnchorProps {
1179
- 'data-sveltekit-noscroll'?: true | undefined | null;
1180
- 'data-sveltekit-preload-code'?: true | 'eager' | 'viewport' | 'hover' | 'tap' | 'off' | undefined | null;
1181
- 'data-sveltekit-preload-data'?: true | 'hover' | 'tap' | 'off' | undefined | null;
1182
- 'data-sveltekit-reload'?: true | undefined | null;
1183
- }
1184
-
1185
1184
  interface SvelteMediaTimeRange {
1186
1185
  start: number;
1187
1186
  end: number;
@@ -1220,7 +1219,7 @@ declare namespace svelte.JSX {
1220
1219
 
1221
1220
  interface IntrinsicElements {
1222
1221
  // HTML
1223
- a: HTMLProps<HTMLAnchorElement> & SapperAnchorProps & SvelteKitAnchorProps;
1222
+ a: HTMLProps<HTMLAnchorElement> & SapperAnchorProps;
1224
1223
  abbr: HTMLProps<HTMLElement>;
1225
1224
  address: HTMLProps<HTMLElement>;
1226
1225
  area: HTMLProps<HTMLAreaElement>;
@@ -1397,7 +1396,7 @@ declare namespace svelte.JSX {
1397
1396
  sveltefragment: { slot?: string; };
1398
1397
  svelteoptions: { [name: string]: any };
1399
1398
  sveltehead: { [name: string]: any };
1400
- svelteelement: { 'this': string | undefined | null; } & HTMLProps<any> & SVGProps<any> & SapperAnchorProps & SvelteKitAnchorProps;
1399
+ svelteelement: { 'this': string | undefined | null; } & HTMLProps<any> & SVGProps<any> & SapperAnchorProps;
1401
1400
  // Needed due to backwards compatibility type which hits these
1402
1401
  'svelte:window': HTMLProps<Window> & SvelteWindowProps;
1403
1402
  'svelte:body': HTMLProps<HTMLElement>;
package/svelte-shims.d.ts CHANGED
@@ -231,6 +231,7 @@ declare function __sveltets_2_createComponentAny(props: Record<string, any>): _S
231
231
  declare function __sveltets_2_any(...dummy: any[]): any;
232
232
  declare function __sveltets_2_empty(...dummy: any[]): {};
233
233
  declare function __sveltets_2_union<T1,T2,T3,T4,T5>(t1:T1,t2?:T2,t3?:T3,t4?:T4,t5?:T5): T1 & T2 & T3 & T4 & T5;
234
+ declare function __sveltets_2_nonNullable<T>(type: T): NonNullable<T>;
234
235
 
235
236
  declare function __sveltets_2_cssProp(prop: Record<string, any>): {};
236
237
 
@@ -295,7 +296,14 @@ declare type ATypedSvelteComponent = {
295
296
  $on(event: string, handler: ((e: any) => any) | null | undefined): () => void;
296
297
  }
297
298
  /**
298
- * Ambient type only used for intellisense, DO NOT USE IN YOUR PROJECT
299
+ * Ambient type only used for intellisense, DO NOT USE IN YOUR PROJECT.
300
+ *
301
+ * If you're looking for the type of a Svelte Component, use `SvelteComponentTyped` and `ComponentType` instead:
302
+ *
303
+ * ```ts
304
+ * import type { ComponentType, SvelteComponentTyped } from "svelte";
305
+ * let myComponentConstructor: ComponentType<SvelteComponentTyped> = ..;
306
+ * ```
299
307
  */
300
308
  declare type ConstructorOfATypedSvelteComponent = new (args: {target: any, props?: any}) => ATypedSvelteComponent
301
309
  declare function __sveltets_2_ensureComponent<T extends ConstructorOfATypedSvelteComponent | null | undefined>(type: T): NonNullable<T>;