svelte2tsx 0.4.4 → 0.4.8

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
@@ -1494,6 +1494,12 @@ function surroundWithIgnoreComments(str) {
1494
1494
  return IGNORE_START_COMMENT + str + IGNORE_END_COMMENT;
1495
1495
  }
1496
1496
 
1497
+ /**
1498
+ * Get the constructor type of a component node
1499
+ * @param node The component node to infer the this type from
1500
+ * @param thisValue If node is svelte:component, you may pass the value
1501
+ * of this={..} to use that instead of the more general componentType
1502
+ */
1497
1503
  function getTypeForComponent(node) {
1498
1504
  if (node.name === 'svelte:component' || node.name === 'svelte:self') {
1499
1505
  return '__sveltets_1_componentType()';
@@ -1502,6 +1508,36 @@ function getTypeForComponent(node) {
1502
1508
  return node.name;
1503
1509
  }
1504
1510
  }
1511
+ /**
1512
+ * Get the instance type of a node from its constructor.
1513
+ */
1514
+ function getInstanceTypeSimple(node, str) {
1515
+ const instanceOf = (str) => `__sveltets_1_instanceOf(${str})`;
1516
+ switch (node.type) {
1517
+ case 'InlineComponent':
1518
+ if (node.name === 'svelte:component' && node.expression) {
1519
+ const thisVal = str.original.substring(node.expression.start, node.expression.end);
1520
+ return `new (${thisVal})({target: __sveltets_1_any(''), props: __sveltets_1_any('')})`;
1521
+ }
1522
+ else if (node.name === 'svelte:component' || node.name === 'svelte:self') {
1523
+ return instanceOf('__sveltets_1_componentType()');
1524
+ }
1525
+ else {
1526
+ return `new ${node.name}({target: __sveltets_1_any(''), props: __sveltets_1_any('')})`;
1527
+ }
1528
+ case 'Element':
1529
+ return instanceOf(`__sveltets_1_ctorOf(__sveltets_1_mapElementTag('${node.name}'))`);
1530
+ case 'Body':
1531
+ return instanceOf('HTMLBodyElement');
1532
+ case 'Slot': // Web Components only
1533
+ return instanceOf('HTMLSlotElement');
1534
+ }
1535
+ }
1536
+ /**
1537
+ * Get the instance type of a node from its constructor.
1538
+ * If it's a component, pass in the exact props. This ensures that
1539
+ * the component instance has the right type in case of generic prop types.
1540
+ */
1505
1541
  function getInstanceType(node, originalStr, replacedPropValues = []) {
1506
1542
  if (node.name === 'svelte:component' || node.name === 'svelte:self') {
1507
1543
  return '__sveltets_1_instanceOf(__sveltets_1_componentType())';
@@ -1564,7 +1600,10 @@ function getNameValuePairsFromAttributes(node, originalStr) {
1564
1600
  return { name, value: name, identifier: name };
1565
1601
  }
1566
1602
  if (val.type === 'Text') {
1567
- return { name, value: `'${val.data || val.raw}'` };
1603
+ const quote = ['"', "'"].includes(originalStr[val.start - 1])
1604
+ ? originalStr[val.start - 1]
1605
+ : "'";
1606
+ return { name, value: `${quote}${val.data || val.raw}${quote}` };
1568
1607
  }
1569
1608
  if (val.type === 'MustacheTag') {
1570
1609
  const valueStr = originalStr.substring(val.start + 1, val.end - 1);
@@ -1599,18 +1638,6 @@ function sanitizePropName(name) {
1599
1638
  .map((char) => (/[0-9A-Za-z$_]/.test(char) ? char : '_'))
1600
1639
  .join('');
1601
1640
  }
1602
- function getThisType(node) {
1603
- switch (node.type) {
1604
- case 'InlineComponent':
1605
- return getTypeForComponent(node);
1606
- case 'Element':
1607
- return `__sveltets_1_ctorOf(__sveltets_1_mapElementTag('${node.name}'))`;
1608
- case 'Body':
1609
- return 'HTMLBodyElement';
1610
- case 'Slot': // Web Components only
1611
- return 'HTMLSlotElement';
1612
- }
1613
- }
1614
1641
  function beforeStart(start) {
1615
1642
  return start - 1;
1616
1643
  }
@@ -1656,11 +1683,12 @@ function usesLet(node) {
1656
1683
  */
1657
1684
  function handleActionDirective(htmlx, str, attr, parent) {
1658
1685
  str.overwrite(attr.start, attr.start + 'use:'.length, '{...__sveltets_1_ensureAction(');
1686
+ const name = parent.name === 'svelte:body' ? 'body' : parent.name;
1659
1687
  if (!attr.expression) {
1660
- str.appendLeft(attr.end, `(__sveltets_1_mapElementTag('${parent.name}')))}`);
1688
+ str.appendLeft(attr.end, `(__sveltets_1_mapElementTag('${name}')))}`);
1661
1689
  return;
1662
1690
  }
1663
- str.overwrite(attr.start + `use:${attr.name}`.length, attr.expression.start, `(__sveltets_1_mapElementTag('${parent.name}'),(`);
1691
+ str.overwrite(attr.start + `use:${attr.name}`.length, attr.expression.start, `(__sveltets_1_mapElementTag('${name}'),(`);
1664
1692
  str.appendLeft(attr.expression.end, ')))');
1665
1693
  const lastChar = htmlx[attr.end - 1];
1666
1694
  if (isQuote(lastChar)) {
@@ -1977,6 +2005,11 @@ const oneWayBindingAttributes = new Map(['clientWidth', 'clientHeight', 'offsetW
1977
2005
  e,
1978
2006
  'HTMLMediaElement'
1979
2007
  ])));
2008
+ /**
2009
+ * List of all binding names that are transformed to sth like `binding = variable`.
2010
+ * This applies to readonly bindings and the this binding.
2011
+ */
2012
+ const assignmentBindings = new Set([...oneWayBindingAttributes.keys(), 'this']);
1980
2013
  /**
1981
2014
  * Transform bind:xxx into something that conforms to JSX
1982
2015
  */
@@ -2002,11 +2035,19 @@ function handleBinding(htmlx, str, attr, el) {
2002
2035
  ];
2003
2036
  //bind this
2004
2037
  if (attr.name === 'this' && supportsBindThis.includes(el.type)) {
2005
- const thisType = getThisType(el);
2038
+ // bind:this is effectively only works bottom up - the variable is updated by the element, not
2039
+ // the other way round. So we check if the instance is assignable to the variable.
2040
+ // Some notes:
2041
+ // - If the component unmounts (it's inside an if block, or svelte:component this={null},
2042
+ // the value becomes null, but we don't add it to the clause because it would introduce
2043
+ // worse DX for the 99% use case, and because null !== undefined which others might use to type the declaration.
2044
+ // - This doesn't do a 100% correct job of infering the instance type in case someone used generics for input props.
2045
+ // For now it errs on the side of "no false positives" at the cost of maybe some missed type bugs
2046
+ const thisType = getInstanceTypeSimple(el, str);
2006
2047
  if (thisType) {
2007
- str.remove(attr.start, attr.expression.start);
2008
- str.appendLeft(attr.expression.start, `{...__sveltets_1_ensureType(${thisType}, `);
2009
- str.overwrite(attr.expression.end, attr.end, ')}');
2048
+ str.overwrite(attr.start, attr.expression.start, '{...__sveltets_1_empty(');
2049
+ const instanceOfThisAssignment = ' = ' + surroundWithIgnoreComments(thisType) + ')}';
2050
+ str.overwrite(attr.expression.end, attr.end, instanceOfThisAssignment);
2010
2051
  return;
2011
2052
  }
2012
2053
  }
@@ -3091,6 +3132,10 @@ function extractIdentifiers(node, identifiers = []) {
3091
3132
  // in ts Ast { a = 1 } and { a } are both ShorthandPropertyAssignment
3092
3133
  extractIdentifiers(child.name, identifiers);
3093
3134
  }
3135
+ else if (ts__default['default'].isPropertyAssignment(child)) {
3136
+ // { a: b }
3137
+ extractIdentifiers(child.initializer, identifiers);
3138
+ }
3094
3139
  });
3095
3140
  }
3096
3141
  else if (ts__default['default'].isArrayLiteralExpression(node)) {
@@ -3164,7 +3209,7 @@ function getNamesFromLabeledStatement(node) {
3164
3209
  // svelte won't let you create a variable with $ prefix (reserved for stores)
3165
3210
  .filter((name) => !name.startsWith('$')));
3166
3211
  }
3167
- function isFirstInAnExpressionStatement(node) {
3212
+ function isSafeToPrefixWithSemicolon(node) {
3168
3213
  let parent = node.parent;
3169
3214
  while (parent && !ts__default['default'].isExpressionStatement(parent)) {
3170
3215
  parent = parent.parent;
@@ -3172,7 +3217,13 @@ function isFirstInAnExpressionStatement(node) {
3172
3217
  if (!parent) {
3173
3218
  return false;
3174
3219
  }
3175
- return parent.getStart() === node.getStart();
3220
+ return (parent.getStart() === node.getStart() &&
3221
+ !(parent.parent &&
3222
+ (ts__default['default'].isIfStatement(parent.parent) ||
3223
+ ts__default['default'].isForStatement(parent.parent) ||
3224
+ ts__default['default'].isForInStatement(parent.parent) ||
3225
+ ts__default['default'].isForOfStatement(parent.parent) ||
3226
+ ts__default['default'].isWhileStatement(parent.parent))));
3176
3227
  }
3177
3228
 
3178
3229
  function is$$EventsDeclaration(node) {
@@ -3471,7 +3522,7 @@ function checkIfImportIsEventDispatcher(node) {
3471
3522
  return;
3472
3523
  }
3473
3524
  const namedImports = (_a = node.importClause) === null || _a === void 0 ? void 0 : _a.namedBindings;
3474
- if (ts__default['default'].isNamedImports(namedImports)) {
3525
+ if (namedImports && ts__default['default'].isNamedImports(namedImports)) {
3475
3526
  const eventDispatcherImport = namedImports.elements.find(
3476
3527
  // If it's an aliased import, propertyName is set
3477
3528
  (el) => (el.propertyName || el.name).text === 'createEventDispatcher');
@@ -3781,9 +3832,15 @@ class ExportedNames {
3781
3832
  getDoc(target) {
3782
3833
  var _a, _b;
3783
3834
  let doc = undefined;
3835
+ // Traverse `a` one up. If the declaration is part of a declaration list,
3836
+ // the comment is at this point already
3837
+ const variableDeclaration = target === null || target === void 0 ? void 0 : target.parent;
3784
3838
  // Traverse `a` up to `export let a`
3785
3839
  const exportExpr = (_b = (_a = target === null || target === void 0 ? void 0 : target.parent) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.parent;
3786
- if (exportExpr) {
3840
+ if (variableDeclaration) {
3841
+ doc = getLastLeadingDoc(variableDeclaration);
3842
+ }
3843
+ if (exportExpr && !doc) {
3787
3844
  doc = getLastLeadingDoc(exportExpr);
3788
3845
  }
3789
3846
  return doc;
@@ -3928,8 +3985,9 @@ function updatePrepends(str, index, toAppend, removeExisting) {
3928
3985
  * were used as stores are appended with `let $xx = __sveltets_1_store_get(xx)` to create the store variables.
3929
3986
  */
3930
3987
  class ImplicitStoreValues {
3931
- constructor(storesResolvedInTemplate = [], renderFunctionStart) {
3988
+ constructor(storesResolvedInTemplate = [], renderFunctionStart, storeFromImportsWrapper = (input) => input) {
3932
3989
  this.renderFunctionStart = renderFunctionStart;
3990
+ this.storeFromImportsWrapper = storeFromImportsWrapper;
3933
3991
  this.accessedStores = new Set();
3934
3992
  this.variableDeclarations = [];
3935
3993
  this.reactiveDeclarations = [];
@@ -3989,7 +4047,7 @@ class ImplicitStoreValues {
3989
4047
  if (!storeNames.length) {
3990
4048
  return;
3991
4049
  }
3992
- const storeDeclarations = surroundWithIgnoreComments(this.createStoreDeclarations(storeNames));
4050
+ const storeDeclarations = this.storeFromImportsWrapper(surroundWithIgnoreComments(this.createStoreDeclarations(storeNames)));
3993
4051
  str.appendRight(this.renderFunctionStart, storeDeclarations);
3994
4052
  }
3995
4053
  createStoreDeclarations(storeNames) {
@@ -4285,10 +4343,19 @@ function handleStore(node, parent, str) {
4285
4343
  }
4286
4344
  return;
4287
4345
  }
4346
+ const dollar = str.original.indexOf('$', node.start);
4347
+ // handle bindings which are transformed to assignments. These need special treatment because
4348
+ // `(__sveltets_1_store_get(foo), foo$) = something` is syntactically invalid
4349
+ // Therefore remove the outer commas. Note: This relies on the binding expression wrapping
4350
+ // this statement with __sveltets_1_empty
4351
+ if (parent.type === 'Binding' && assignmentBindings.has(parent.name)) {
4352
+ str.overwrite(dollar, dollar + 1, '__sveltets_1_store_get(', { contentOnly: true });
4353
+ str.prependLeft(node.end, `), $${storename}`);
4354
+ return;
4355
+ }
4288
4356
  // we change "$store" references into "(__sveltets_1_store_get(store), $store)"
4289
4357
  // - in order to get ts errors if store is not assignable to SvelteStore
4290
4358
  // - use $store variable defined above to get ts flow control
4291
- const dollar = str.original.indexOf('$', node.start);
4292
4359
  str.overwrite(dollar, dollar + 1, '(__sveltets_1_store_get(', { contentOnly: true });
4293
4360
  str.prependLeft(node.end, `), $${storename})`);
4294
4361
  }
@@ -4413,8 +4480,11 @@ class ImplicitTopLevelNames {
4413
4480
  }
4414
4481
  const start = expression.getStart() + this.astOffset;
4415
4482
  const end = expression.getEnd() + this.astOffset;
4416
- // $: a = { .. } / $: a = .. as .. => () => ( .. )
4417
- if (ts__default['default'].isObjectLiteralExpression(expression) || ts__default['default'].isAsExpression(expression)) {
4483
+ // $: a = { .. }.. / $: a = .. as .. => () => ( .. )
4484
+ if (ts__default['default'].isObjectLiteralExpression(expression) ||
4485
+ (expression.getText().startsWith('{') &&
4486
+ this.isNodeStartsWithObjectLiteral(expression)) ||
4487
+ ts__default['default'].isAsExpression(expression)) {
4418
4488
  this.str.appendLeft(start, '(');
4419
4489
  this.str.appendRight(end, ')');
4420
4490
  }
@@ -4423,6 +4493,24 @@ class ImplicitTopLevelNames {
4423
4493
  // Not adding ';' at the end because right now this function is only invoked
4424
4494
  // in situations where there is a line break of ; guaranteed to be present (else the code is invalid)
4425
4495
  }
4496
+ isNodeStartsWithObjectLiteral(node) {
4497
+ if (ts__default['default'].isObjectLiteralExpression(node)) {
4498
+ return true;
4499
+ }
4500
+ if (ts__default['default'].isElementAccessExpression(node)) {
4501
+ return this.isNodeStartsWithObjectLiteral(node.expression);
4502
+ }
4503
+ if (ts__default['default'].isBinaryExpression(node)) {
4504
+ return this.isNodeStartsWithObjectLiteral(node.left);
4505
+ }
4506
+ if (ts__default['default'].isConditionalExpression(node)) {
4507
+ return this.isNodeStartsWithObjectLiteral(node.condition);
4508
+ }
4509
+ return node
4510
+ .getChildren()
4511
+ .filter((e) => e.pos === node.pos)
4512
+ .some((child) => this.isNodeStartsWithObjectLiteral(child));
4513
+ }
4426
4514
  modifyCode(rootVariables) {
4427
4515
  for (const node of this.map.values()) {
4428
4516
  const names = getNamesFromLabeledStatement(node);
@@ -4605,7 +4693,7 @@ class Generics {
4605
4693
  }
4606
4694
  }
4607
4695
 
4608
- function processInstanceScriptContent(str, script, events, implicitStoreValues) {
4696
+ function processInstanceScriptContent(str, script, events, implicitStoreValues, mode) {
4609
4697
  const htmlx = str.original;
4610
4698
  const scriptContent = htmlx.substring(script.content.start, script.content.end);
4611
4699
  const tsAst = ts__namespace.createSourceFile('component.ts.svelte', scriptContent, ts__namespace.ScriptTarget.Latest, true, ts__namespace.ScriptKind.TS);
@@ -4676,8 +4764,12 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues)
4676
4764
  }
4677
4765
  // handle $store++, $store--, ++$store, --$store
4678
4766
  if ((ts__namespace.isPrefixUnaryExpression(parent) || ts__namespace.isPostfixUnaryExpression(parent)) &&
4679
- parent.operator !==
4680
- ts__namespace.SyntaxKind.ExclamationToken /* `!$store` does not need processing */) {
4767
+ ![
4768
+ ts__namespace.SyntaxKind.ExclamationToken,
4769
+ ts__namespace.SyntaxKind.PlusToken,
4770
+ ts__namespace.SyntaxKind.MinusToken,
4771
+ ts__namespace.SyntaxKind.TildeToken // ~$store
4772
+ ].includes(parent.operator) /* `!$store` etc does not need processing */) {
4681
4773
  let simpleOperator;
4682
4774
  if (parent.operator === ts__namespace.SyntaxKind.PlusPlusToken) {
4683
4775
  simpleOperator = '+';
@@ -4700,7 +4792,7 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues)
4700
4792
  // - in order to get ts errors if store is not assignable to SvelteStore
4701
4793
  // - use $store variable defined above to get ts flow control
4702
4794
  const dollar = str.original.indexOf('$', ident.getStart() + astOffset);
4703
- const getPrefix = isFirstInAnExpressionStatement(ident) ? ';' : '';
4795
+ const getPrefix = isSafeToPrefixWithSemicolon(ident) ? ';' : '';
4704
4796
  str.overwrite(dollar, dollar + 1, getPrefix + '(__sveltets_1_store_get(');
4705
4797
  str.prependLeft(ident.end + astOffset, `), $${storename})`);
4706
4798
  };
@@ -4808,7 +4900,7 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues)
4808
4900
  if (ts__namespace.isImportEqualsDeclaration(node)) {
4809
4901
  const end = node.getEnd() + astOffset;
4810
4902
  if (str.original[end - 1] !== ';') {
4811
- str.appendLeft(end, ';');
4903
+ preprendStr(str, end, ';');
4812
4904
  }
4813
4905
  }
4814
4906
  if (ts__namespace.isVariableDeclaration(node)) {
@@ -4874,6 +4966,13 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues)
4874
4966
  if (firstImport) {
4875
4967
  str.appendRight(firstImport.getStart() + astOffset, '\n');
4876
4968
  }
4969
+ if (mode === 'dts') {
4970
+ // Transform interface declarations to type declarations because indirectly
4971
+ // using interfaces inside the return type of a function is forbidden.
4972
+ // This is not a problem for intellisense/type inference but it will
4973
+ // break dts generation (file will not be generated).
4974
+ transformInterfacesToTypes(tsAst, str, astOffset);
4975
+ }
4877
4976
  return {
4878
4977
  exportedNames,
4879
4978
  events,
@@ -4884,6 +4983,26 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues)
4884
4983
  generics
4885
4984
  };
4886
4985
  }
4986
+ function transformInterfacesToTypes(tsAst, str, astOffset) {
4987
+ tsAst.statements.filter(ts__namespace.isInterfaceDeclaration).forEach((node) => {
4988
+ var _a;
4989
+ str.overwrite(node.getStart() + astOffset, node.getStart() + astOffset + 'interface'.length, 'type');
4990
+ if ((_a = node.heritageClauses) === null || _a === void 0 ? void 0 : _a.length) {
4991
+ const extendsStart = node.heritageClauses[0].getStart() + astOffset;
4992
+ str.overwrite(extendsStart, extendsStart + 'extends'.length, '=');
4993
+ const extendsList = node.heritageClauses[0].types;
4994
+ let prev = extendsList[0];
4995
+ extendsList.slice(1).forEach((heritageClause) => {
4996
+ str.overwrite(prev.getEnd() + astOffset, heritageClause.getStart() + astOffset, ' & ');
4997
+ prev = heritageClause;
4998
+ });
4999
+ str.appendLeft(node.heritageClauses[0].getEnd() + astOffset, ' & ');
5000
+ }
5001
+ else {
5002
+ str.prependLeft(str.original.indexOf('{', node.getStart() + astOffset), '=');
5003
+ }
5004
+ });
5005
+ }
4887
5006
 
4888
5007
  function processModuleScriptTag(str, script, implicitStoreValues) {
4889
5008
  const htmlx = str.original;
@@ -5373,7 +5492,7 @@ function svelte2tsx(svelte, options = {}) {
5373
5492
  if (scriptTag.start != instanceScriptTarget) {
5374
5493
  str.move(scriptTag.start, scriptTag.end, instanceScriptTarget);
5375
5494
  }
5376
- const res = processInstanceScriptContent(str, scriptTag, events, implicitStoreValues);
5495
+ const res = processInstanceScriptContent(str, scriptTag, events, implicitStoreValues, options.mode);
5377
5496
  uses$$props = uses$$props || res.uses$$props;
5378
5497
  uses$$restProps = uses$$restProps || res.uses$$restProps;
5379
5498
  uses$$slots = uses$$slots || res.uses$$slots;
@@ -5397,7 +5516,7 @@ function svelte2tsx(svelte, options = {}) {
5397
5516
  });
5398
5517
  // we need to process the module script after the instance script has moved otherwise we get warnings about moving edited items
5399
5518
  if (moduleScriptTag) {
5400
- processModuleScriptTag(str, moduleScriptTag, new ImplicitStoreValues(implicitStoreValues.getAccessedStores(), renderFunctionStart));
5519
+ processModuleScriptTag(str, moduleScriptTag, new ImplicitStoreValues(implicitStoreValues.getAccessedStores(), renderFunctionStart, scriptTag ? undefined : (input) => `</>;${input}<>`));
5401
5520
  }
5402
5521
  addComponentExport({
5403
5522
  str,