svelte2tsx 0.5.8 → 0.5.11

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
@@ -2096,7 +2096,7 @@ const oneWayBindingAttributes$1 = new Map(['clientWidth', 'clientHeight', 'offse
2096
2096
  * List of all binding names that are transformed to sth like `binding = variable`.
2097
2097
  * This applies to readonly bindings and the this binding.
2098
2098
  */
2099
- const assignmentBindings = new Set([...oneWayBindingAttributes$1.keys(), 'this']);
2099
+ new Set([...oneWayBindingAttributes$1.keys(), 'this']);
2100
2100
  /**
2101
2101
  * Transform bind:xxx into something that conforms to JSX
2102
2102
  */
@@ -2440,22 +2440,42 @@ function handleEventHandler$1(htmlx, str, attr, parent) {
2440
2440
  */
2441
2441
  function handleIf$1(htmlx, str, ifBlock, ifScope) {
2442
2442
  const endIf = htmlx.lastIndexOf('{', ifBlock.end - 1);
2443
+ const constTags = extractConstTags(ifBlock.children);
2444
+ const ifConditionEnd = htmlx.indexOf('}', ifBlock.expression.end) + 1;
2445
+ const hasConstTags = !!constTags.length;
2446
+ const endIIFE = createEndIIFE(hasConstTags);
2447
+ const startIIFE = createStartIIFE(hasConstTags);
2448
+ if (hasConstTags) {
2449
+ // {@const hi = exp} <div>{hi}> -> {(() => { const hi = exp; return <> <div>{hi}<div></> })}
2450
+ constTags.forEach((constTag) => {
2451
+ constTag(ifConditionEnd, str);
2452
+ });
2453
+ str.appendRight(ifConditionEnd, 'return <>');
2454
+ if (ifBlock.else) {
2455
+ // {:else} -> </>})()}</> : <>
2456
+ const elseWord = htmlx.lastIndexOf(':else', ifBlock.else.start);
2457
+ const elseStart = htmlx.lastIndexOf('{', elseWord);
2458
+ str.appendLeft(elseStart, endIIFE);
2459
+ }
2460
+ }
2443
2461
  if (ifBlock.elseif) {
2444
2462
  // {:else if expr} -> : (expr) ? <>
2463
+ // {:else if expr}{@const ...} -> : (expr) ? <>{(() => {const ...; return <>
2445
2464
  const elseIfStart = htmlx.lastIndexOf('{', ifBlock.expression.start);
2446
- const elseIfConditionEnd = htmlx.indexOf('}', ifBlock.expression.end) + 1;
2447
- str.overwrite(elseIfStart, ifBlock.expression.start, '</> : (', { contentOnly: true });
2448
- str.overwrite(withTrailingPropertyAccess$1(str.original, ifBlock.expression.end), elseIfConditionEnd, ') ? <>');
2465
+ str.overwrite(elseIfStart, ifBlock.expression.start, '</> : (', {
2466
+ contentOnly: true
2467
+ });
2468
+ str.overwrite(withTrailingPropertyAccess$1(str.original, ifBlock.expression.end), ifConditionEnd, ') ? <>' + startIIFE);
2449
2469
  ifScope.addElseIf(ifBlock.expression, str);
2450
2470
  if (!ifBlock.else) {
2451
- str.appendLeft(endIf, '</> : <>');
2471
+ str.appendLeft(endIf, endIIFE + '</> : <>');
2452
2472
  }
2453
2473
  return;
2454
2474
  }
2455
2475
  // {#if expr} -> {(expr) ? <>
2476
+ // {#if expr}{@const ...} -> {(expr) ? <>{(() => {const ...; return <>
2456
2477
  str.overwrite(ifBlock.start, ifBlock.expression.start, '{(', { contentOnly: true });
2457
- const end = htmlx.indexOf('}', ifBlock.expression.end);
2458
- str.overwrite(withTrailingPropertyAccess$1(str.original, ifBlock.expression.end), end + 1, ') ? <>', { contentOnly: true });
2478
+ str.overwrite(withTrailingPropertyAccess$1(str.original, ifBlock.expression.end), ifConditionEnd, ') ? <>' + startIIFE, { contentOnly: true });
2459
2479
  ifScope.addNestedIf(ifBlock.expression, str);
2460
2480
  if (ifBlock.else) {
2461
2481
  // {/if} -> </> }
@@ -2463,11 +2483,21 @@ function handleIf$1(htmlx, str, ifBlock, ifScope) {
2463
2483
  }
2464
2484
  else {
2465
2485
  // {/if} -> </> : <></>}
2466
- str.overwrite(endIf, ifBlock.end, '</> : <></>}', { contentOnly: true });
2486
+ // {@const ...} -> </>})()}</> : <></>}
2487
+ str.overwrite(endIf, ifBlock.end, endIIFE + '</> : <></>}', {
2488
+ contentOnly: true
2489
+ });
2467
2490
  }
2468
2491
  }
2492
+ function createStartIIFE(hasConstTags) {
2493
+ return hasConstTags ? '{(() => {' : '';
2494
+ }
2495
+ function createEndIIFE(hasConstTags) {
2496
+ return hasConstTags ? '</>})()}' : '';
2497
+ }
2469
2498
  /**
2470
2499
  * {:else} ---> </> : <>
2500
+ * {:else} {@const ...} -> </> : <>{(() => { const ...; return<>
2471
2501
  */
2472
2502
  function handleElse$1(htmlx, str, elseBlock, parent, ifScope) {
2473
2503
  var _a, _b;
@@ -2478,8 +2508,18 @@ function handleElse$1(htmlx, str, elseBlock, parent, ifScope) {
2478
2508
  const elseEnd = htmlx.lastIndexOf('}', elseBlock.start);
2479
2509
  const elseword = htmlx.lastIndexOf(':else', elseEnd);
2480
2510
  const elseStart = htmlx.lastIndexOf('{', elseword);
2481
- str.overwrite(elseStart, elseEnd + 1, '</> : <>');
2511
+ const constTags = extractConstTags(elseBlock.children);
2512
+ const hasConstTags = !!constTags.length;
2513
+ str.overwrite(elseStart, elseEnd + 1, '</> : <>' + createStartIIFE(hasConstTags));
2482
2514
  ifScope.addElse();
2515
+ if (!hasConstTags) {
2516
+ return;
2517
+ }
2518
+ constTags.forEach((constTag) => {
2519
+ constTag(elseEnd + 1, str);
2520
+ });
2521
+ str.appendRight(elseEnd + 1, 'return <>');
2522
+ str.appendLeft(elseBlock.end, createEndIIFE(true));
2483
2523
  }
2484
2524
 
2485
2525
  var IfType;
@@ -3451,7 +3491,6 @@ class Element {
3451
3491
  case 'svelte:head':
3452
3492
  case 'svelte:window':
3453
3493
  case 'svelte:body':
3454
- case 'svelte:element':
3455
3494
  case 'svelte:fragment': {
3456
3495
  // remove the colon: svelte:xxx -> sveltexxx
3457
3496
  const nodeName = `svelte${this.node.name.substring(7)}`;
@@ -3460,6 +3499,18 @@ class Element {
3460
3499
  this.addNameConstDeclaration = () => (this.startTransformation[0] = `{ const ${this._name} = ${createElement}("${nodeName}", {`);
3461
3500
  break;
3462
3501
  }
3502
+ case 'svelte:element': {
3503
+ const nodeName = this.node.tag
3504
+ ? typeof this.node.tag !== 'string'
3505
+ ? [this.node.tag.start, this.node.tag.end]
3506
+ : `"${this.node.tag}"`
3507
+ : '""';
3508
+ this._name = '$$_svelteelement' + this.computeDepth();
3509
+ this.startTransformation.push(`{ ${createElement}(`, nodeName, ', {');
3510
+ this.addNameConstDeclaration = () => ((this.startTransformation[0] = `{ const ${this._name} = ${createElement}(`),
3511
+ ', {');
3512
+ break;
3513
+ }
3463
3514
  case 'slot': {
3464
3515
  // If the element is a <slot> tag, create the element with the createSlot-function
3465
3516
  // which is created inside createRenderFunction.ts to check that the name and attributes
@@ -3914,7 +3965,7 @@ function handleAttribute(str, attr, parent, preserveCase, element) {
3914
3965
  return;
3915
3966
  }
3916
3967
  if (attr.value.length == 0) {
3917
- // attr=""
3968
+ // shouldn't happen
3918
3969
  addAttribute(attributeName, ['""']);
3919
3970
  return;
3920
3971
  }
@@ -3922,6 +3973,11 @@ function handleAttribute(str, attr, parent, preserveCase, element) {
3922
3973
  if (attr.value.length == 1) {
3923
3974
  const attrVal = attr.value[0];
3924
3975
  if (attrVal.type == 'Text') {
3976
+ // Handle the attr="" special case with a transformation that allows mapping of the position
3977
+ if (attrVal.start === attrVal.end) {
3978
+ addAttribute(attributeName, [[attrVal.start - 1, attrVal.end + 1]]);
3979
+ return;
3980
+ }
3925
3981
  const hasBrackets = str.original.lastIndexOf('}', attrVal.end) === attrVal.end - 1 ||
3926
3982
  str.original.lastIndexOf('}"', attrVal.end) === attrVal.end - 1 ||
3927
3983
  str.original.lastIndexOf("}'", attrVal.end) === attrVal.end - 1;
@@ -4802,22 +4858,6 @@ function getNamesFromLabeledStatement(node) {
4802
4858
  // svelte won't let you create a variable with $ prefix (reserved for stores)
4803
4859
  .filter((name) => !name.startsWith('$')));
4804
4860
  }
4805
- function isSafeToPrefixWithSemicolon(node) {
4806
- let parent = node.parent;
4807
- while (parent && !ts__default['default'].isExpressionStatement(parent)) {
4808
- parent = parent.parent;
4809
- }
4810
- if (!parent) {
4811
- return false;
4812
- }
4813
- return (parent.getStart() === node.getStart() &&
4814
- !(parent.parent &&
4815
- (ts__default['default'].isIfStatement(parent.parent) ||
4816
- ts__default['default'].isForStatement(parent.parent) ||
4817
- ts__default['default'].isForInStatement(parent.parent) ||
4818
- ts__default['default'].isForOfStatement(parent.parent) ||
4819
- ts__default['default'].isWhileStatement(parent.parent))));
4820
- }
4821
4861
 
4822
4862
  function is$$EventsDeclaration(node) {
4823
4863
  return isInterfaceOrTypeDeclaration(node) && node.name.text === '$$Events';
@@ -5922,72 +5962,12 @@ function getSingleSlotDef(componentNode, slotName) {
5922
5962
  return `__sveltets_1_instanceOf(${componentType}).$$slot_def['${slotName}']`;
5923
5963
  }
5924
5964
 
5925
- function handleStore(node, parent, str) {
5926
- const storename = node.name.slice(1);
5927
- //handle assign to
5928
- if (parent.type == 'AssignmentExpression' && parent.left == node && parent.operator == '=') {
5929
- const dollar = str.original.indexOf('$', node.start);
5930
- str.remove(dollar, dollar + 1);
5931
- str.overwrite(node.end, str.original.indexOf('=', node.end) + 1, '.set(');
5932
- str.appendLeft(parent.end, ')');
5933
- return;
5934
- }
5935
- // handle Assignment operators ($store +=, -=, *=, /=, %=, **=, etc.)
5936
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Assignment
5937
- const operators = ['+=', '-=', '*=', '/=', '%=', '**=', '<<=', '>>=', '>>>=', '&=', '^=', '|='];
5938
- if (parent.type == 'AssignmentExpression' &&
5939
- parent.left == node &&
5940
- operators.includes(parent.operator)) {
5941
- const storename = node.name.slice(1); // drop the $
5942
- const operator = parent.operator.substring(0, parent.operator.length - 1); // drop the = sign
5943
- str.overwrite(parent.start, str.original.indexOf('=', node.end) + 1, `${storename}.set( $${storename} ${operator}`);
5944
- str.appendLeft(parent.end, ')');
5945
- return;
5946
- }
5947
- // handle $store++, $store--, ++$store, --$store
5948
- if (parent.type == 'UpdateExpression') {
5949
- let simpleOperator;
5950
- if (parent.operator === '++') {
5951
- simpleOperator = '+';
5952
- }
5953
- if (parent.operator === '--') {
5954
- simpleOperator = '-';
5955
- }
5956
- if (simpleOperator) {
5957
- const storename = node.name.slice(1); // drop the $
5958
- str.overwrite(parent.start, parent.end, `(${storename}.set( $${storename} ${simpleOperator} 1), $${storename})`);
5959
- }
5960
- else {
5961
- console.warn(`Warning - unrecognized UpdateExpression operator ${parent.operator}!
5962
- This is an edge case unaccounted for in svelte2tsx, please file an issue:
5963
- https://github.com/sveltejs/language-tools/issues/new/choose
5964
- `, str.original.slice(parent.start, parent.end));
5965
- }
5966
- return;
5967
- }
5968
- const dollar = str.original.indexOf('$', node.start);
5969
- // handle bindings which are transformed to assignments. These need special treatment because
5970
- // `(__sveltets_1_store_get(foo), foo$) = something` is syntactically invalid
5971
- // Therefore remove the outer commas. Note: This relies on the binding expression wrapping
5972
- // this statement with __sveltets_1_empty
5973
- if (parent.type === 'Binding' && assignmentBindings.has(parent.name)) {
5974
- str.overwrite(dollar, dollar + 1, '__sveltets_1_store_get(', { contentOnly: true });
5975
- str.prependLeft(node.end, `), $${storename}`);
5976
- return;
5977
- }
5978
- // we change "$store" references into "(__sveltets_1_store_get(store), $store)"
5979
- // - in order to get ts errors if store is not assignable to SvelteStore
5980
- // - use $store variable defined above to get ts flow control
5981
- str.overwrite(dollar, dollar + 1, '(__sveltets_1_store_get(', { contentOnly: true });
5982
- str.prependLeft(node.end, `), $${storename})`);
5983
- }
5984
5965
  const reservedNames = new Set(['$$props', '$$restProps', '$$slots']);
5985
5966
  class Stores {
5986
- constructor(scope, str, isDeclaration) {
5967
+ constructor(scope, isDeclaration) {
5987
5968
  this.scope = scope;
5988
- this.str = str;
5989
5969
  this.isDeclaration = isDeclaration;
5990
- this.pendingStoreResolutions = [];
5970
+ this.possibleStores = [];
5991
5971
  }
5992
5972
  handleDirective(node, str) {
5993
5973
  if (this.notAStore(node.name) || this.isDeclaration.value) {
@@ -5995,7 +5975,7 @@ class Stores {
5995
5975
  }
5996
5976
  const start = str.original.indexOf('$', node.start);
5997
5977
  const end = start + node.name.length;
5998
- this.pendingStoreResolutions.push({
5978
+ this.possibleStores.push({
5999
5979
  node: { type: 'Identifier', start, end, name: node.name },
6000
5980
  parent: { start: 0, end: 0, type: '' },
6001
5981
  scope: this.scope.current
@@ -6019,18 +5999,17 @@ class Stores {
6019
5999
  if (isObjectKey(parent, prop)) {
6020
6000
  return;
6021
6001
  }
6022
- this.pendingStoreResolutions.push({ node, parent, scope: this.scope.current });
6002
+ this.possibleStores.push({ node, parent, scope: this.scope.current });
6023
6003
  }
6024
6004
  }
6025
- resolveStores() {
6026
- const unresolvedStores = this.pendingStoreResolutions.filter(({ node, scope }) => {
6005
+ getStoreNames() {
6006
+ const stores = this.possibleStores.filter(({ node, scope }) => {
6027
6007
  const name = node.name;
6028
6008
  // if variable starting with '$' was manually declared by the user,
6029
6009
  // this isn't a store access.
6030
6010
  return !scope.hasDefined(name);
6031
6011
  });
6032
- unresolvedStores.forEach(({ node, parent }) => handleStore(node, parent, this.str));
6033
- return unresolvedStores.map(({ node }) => node.name.slice(1));
6012
+ return stores.map(({ node }) => node.name.slice(1));
6034
6013
  }
6035
6014
  notAStore(name) {
6036
6015
  return name[0] !== '$' || reservedNames.has(name);
@@ -6163,12 +6142,15 @@ class ImplicitTopLevelNames {
6163
6142
  // to `let {a} = b;` produces valid code.
6164
6143
  if (ts__default['default'].isExpressionStatement(node.statement) &&
6165
6144
  isParenthesizedObjectOrArrayLiteralExpression(node.statement.expression)) {
6166
- const start = node.statement.expression.getStart() + this.astOffset;
6167
- this.str.overwrite(start, start + 1, '', { contentOnly: true });
6168
- const end = node.statement.expression.getEnd() + this.astOffset - 1;
6145
+ const parenthesizedExpression = node.statement.expression;
6146
+ const parenthesisStart = parenthesizedExpression.getStart() + this.astOffset;
6147
+ const expressionStart = parenthesizedExpression.expression.getStart() + this.astOffset;
6148
+ this.str.overwrite(parenthesisStart, expressionStart, '', { contentOnly: true });
6149
+ const parenthesisEnd = parenthesizedExpression.getEnd() + this.astOffset;
6150
+ const expressionEnd = parenthesizedExpression.expression.getEnd() + this.astOffset;
6169
6151
  // We need to keep the `)` of the "wrap with invalidate" expression above.
6170
6152
  // We overwrite the same range so it's needed.
6171
- overwriteStr(this.str, end, end + 1, ')', true);
6153
+ overwriteStr(this.str, expressionEnd, parenthesisEnd, ')', true);
6172
6154
  }
6173
6155
  }
6174
6156
  }
@@ -6318,9 +6300,15 @@ class Generics {
6318
6300
  /**
6319
6301
  * move imports to top of script so they appear outside our render function
6320
6302
  */
6321
- function handleImportDeclaration(node, str, astOffset, scriptStart) {
6303
+ function handleImportDeclaration(node, str, astOffset, scriptStart, sourceFile) {
6322
6304
  var _a;
6305
+ const scanner = ts__default['default'].createScanner(sourceFile.languageVersion,
6306
+ /*skipTrivia*/ false, sourceFile.languageVariant);
6323
6307
  const comments = (_a = ts__default['default'].getLeadingCommentRanges(node.getFullText(), 0)) !== null && _a !== void 0 ? _a : [];
6308
+ if (!comments.some((comment) => comment.hasTrailingNewLine) &&
6309
+ isNewGroup(sourceFile, node, scanner)) {
6310
+ str.appendRight(node.getStart() + astOffset, '\n');
6311
+ }
6324
6312
  for (const comment of comments) {
6325
6313
  const commentEnd = node.pos + comment.end + astOffset;
6326
6314
  str.move(node.pos + comment.pos + astOffset, commentEnd, scriptStart + 1);
@@ -6333,8 +6321,27 @@ function handleImportDeclaration(node, str, astOffset, scriptStart) {
6333
6321
  const originalEndChar = str.original[node.end + astOffset - 1];
6334
6322
  str.overwrite(node.end + astOffset - 1, node.end + astOffset, originalEndChar + '\n');
6335
6323
  }
6324
+ /**
6325
+ * adopted from https://github.com/microsoft/TypeScript/blob/6e0447fdf165b1cec9fc80802abcc15bd23a268f/src/services/organizeImports.ts#L111
6326
+ */
6327
+ function isNewGroup(sourceFile, topLevelImportDecl, scanner) {
6328
+ const startPos = topLevelImportDecl.getFullStart();
6329
+ const endPos = topLevelImportDecl.getStart();
6330
+ scanner.setText(sourceFile.text, startPos, endPos - startPos);
6331
+ let numberOfNewLines = 0;
6332
+ while (scanner.getTokenPos() < endPos) {
6333
+ const tokenKind = scanner.scan();
6334
+ if (tokenKind === ts__default['default'].SyntaxKind.NewLineTrivia) {
6335
+ numberOfNewLines++;
6336
+ if (numberOfNewLines >= 2) {
6337
+ return true;
6338
+ }
6339
+ }
6340
+ }
6341
+ return false;
6342
+ }
6336
6343
 
6337
- function processInstanceScriptContent(str, script, events, implicitStoreValues, mode) {
6344
+ function processInstanceScriptContent(str, script, events, implicitStoreValues, mode, hasModuleScript) {
6338
6345
  const htmlx = str.original;
6339
6346
  const scriptContent = htmlx.substring(script.content.start, script.content.end);
6340
6347
  const tsAst = ts__namespace.createSourceFile('component.ts.svelte', scriptContent, ts__namespace.ScriptTarget.Latest, true, ts__namespace.ScriptKind.TS);
@@ -6355,95 +6362,8 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6355
6362
  const rootScope = scope;
6356
6363
  const pushScope = () => (scope = new Scope(scope));
6357
6364
  const popScope = () => (scope = scope.parent);
6358
- const handleStore = (ident, parent) => {
6359
- // ignore "typeof $store"
6360
- if (parent && parent.kind === ts__namespace.SyntaxKind.TypeQuery) {
6361
- return;
6362
- }
6363
- // ignore break
6364
- if (parent && parent.kind === ts__namespace.SyntaxKind.BreakStatement) {
6365
- return;
6366
- }
6367
- const storename = ident.getText().slice(1); // drop the $
6368
- // handle assign to
6369
- if (parent &&
6370
- ts__namespace.isBinaryExpression(parent) &&
6371
- parent.operatorToken.kind == ts__namespace.SyntaxKind.EqualsToken &&
6372
- parent.left == ident) {
6373
- //remove $
6374
- const dollar = str.original.indexOf('$', ident.getStart() + astOffset);
6375
- str.remove(dollar, dollar + 1);
6376
- // replace = with .set(
6377
- str.overwrite(ident.end + astOffset, parent.operatorToken.end + astOffset, '.set(');
6378
- // append )
6379
- str.appendLeft(parent.end + astOffset, ')');
6380
- return;
6381
- }
6382
- // handle Assignment operators ($store +=, -=, *=, /=, %=, **=, etc.)
6383
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Assignment
6384
- const operators = {
6385
- [ts__namespace.SyntaxKind.PlusEqualsToken]: '+',
6386
- [ts__namespace.SyntaxKind.MinusEqualsToken]: '-',
6387
- [ts__namespace.SyntaxKind.AsteriskEqualsToken]: '*',
6388
- [ts__namespace.SyntaxKind.SlashEqualsToken]: '/',
6389
- [ts__namespace.SyntaxKind.PercentEqualsToken]: '%',
6390
- [ts__namespace.SyntaxKind.AsteriskAsteriskEqualsToken]: '**',
6391
- [ts__namespace.SyntaxKind.LessThanLessThanEqualsToken]: '<<',
6392
- [ts__namespace.SyntaxKind.GreaterThanGreaterThanEqualsToken]: '>>',
6393
- [ts__namespace.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken]: '>>>',
6394
- [ts__namespace.SyntaxKind.AmpersandEqualsToken]: '&',
6395
- [ts__namespace.SyntaxKind.CaretEqualsToken]: '^',
6396
- [ts__namespace.SyntaxKind.BarEqualsToken]: '|'
6397
- };
6398
- if (ts__namespace.isBinaryExpression(parent) &&
6399
- parent.left == ident &&
6400
- Object.keys(operators).find((x) => x === String(parent.operatorToken.kind))) {
6401
- const operator = operators[parent.operatorToken.kind];
6402
- str.overwrite(parent.getStart() + astOffset, str.original.indexOf('=', ident.end + astOffset) + 1, `${storename}.set( $${storename} ${operator}`);
6403
- str.appendLeft(parent.end + astOffset, ')');
6404
- return;
6405
- }
6406
- // handle $store++, $store--, ++$store, --$store
6407
- if ((ts__namespace.isPrefixUnaryExpression(parent) || ts__namespace.isPostfixUnaryExpression(parent)) &&
6408
- ![
6409
- ts__namespace.SyntaxKind.ExclamationToken,
6410
- ts__namespace.SyntaxKind.PlusToken,
6411
- ts__namespace.SyntaxKind.MinusToken,
6412
- ts__namespace.SyntaxKind.TildeToken // ~$store
6413
- ].includes(parent.operator) /* `!$store` etc does not need processing */) {
6414
- let simpleOperator;
6415
- if (parent.operator === ts__namespace.SyntaxKind.PlusPlusToken) {
6416
- simpleOperator = '+';
6417
- }
6418
- if (parent.operator === ts__namespace.SyntaxKind.MinusMinusToken) {
6419
- simpleOperator = '-';
6420
- }
6421
- if (simpleOperator) {
6422
- str.overwrite(parent.getStart() + astOffset, parent.end + astOffset, `(${storename}.set( $${storename} ${simpleOperator} 1), $${storename})`);
6423
- return;
6424
- }
6425
- else {
6426
- console.warn(`Warning - unrecognized UnaryExpression operator ${parent.operator}!
6427
- This is an edge case unaccounted for in svelte2tsx, please file an issue:
6428
- https://github.com/sveltejs/language-tools/issues/new/choose
6429
- `, parent.getText());
6430
- }
6431
- }
6432
- // we change "$store" references into "(__sveltets_1_store_get(store), $store)"
6433
- // - in order to get ts errors if store is not assignable to SvelteStore
6434
- // - use $store variable defined above to get ts flow control
6435
- const dollar = str.original.indexOf('$', ident.getStart() + astOffset);
6436
- const getPrefix = isSafeToPrefixWithSemicolon(ident)
6437
- ? ';'
6438
- : ts__namespace.isShorthandPropertyAssignment(parent)
6439
- ? // { $store } --> { $store: __sveltets_1_store_get(..)}
6440
- ident.text + ': '
6441
- : '';
6442
- str.overwrite(dollar, dollar + 1, getPrefix + '(__sveltets_1_store_get(');
6443
- str.prependLeft(ident.end + astOffset, `), $${storename})`);
6444
- };
6445
6365
  const resolveStore = (pending) => {
6446
- let { node, parent, scope } = pending;
6366
+ let { node, scope } = pending;
6447
6367
  const name = node.text;
6448
6368
  while (scope) {
6449
6369
  if (scope.declared.has(name)) {
@@ -6452,8 +6372,6 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6452
6372
  }
6453
6373
  scope = scope.parent;
6454
6374
  }
6455
- //We haven't been resolved, we must be a store read/write, handle it.
6456
- handleStore(node, parent);
6457
6375
  const storename = node.getText().slice(1);
6458
6376
  implicitStoreValues.addStoreAcess(storename);
6459
6377
  };
@@ -6530,7 +6448,7 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6530
6448
  exportedNames.handleExportDeclaration(node);
6531
6449
  }
6532
6450
  if (ts__namespace.isImportDeclaration(node)) {
6533
- handleImportDeclaration(node, str, astOffset, script.start);
6451
+ handleImportDeclaration(node, str, astOffset, script.start, tsAst);
6534
6452
  // Check if import is the event dispatcher
6535
6453
  events.checkIfImportIsEventDispatcher(node);
6536
6454
  }
@@ -6603,7 +6521,9 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6603
6521
  .filter(ts__namespace.isImportDeclaration)
6604
6522
  .sort((a, b) => a.end - b.end)[0];
6605
6523
  if (firstImport) {
6606
- str.appendRight(firstImport.getStart() + astOffset, '\n');
6524
+ // ensure it's in a newline.
6525
+ // if file has module script ensure an empty line to separate imports
6526
+ str.appendRight(firstImport.getStart() + astOffset, '\n' + (hasModuleScript ? '\n' : ''));
6607
6527
  }
6608
6528
  if (mode === 'dts') {
6609
6529
  // Transform interface declarations to type declarations because indirectly
@@ -6927,7 +6847,7 @@ function processSvelteTemplate(str, options) {
6927
6847
  //track $store variables since we are only supposed to give top level scopes special treatment, and users can declare $blah variables at higher scopes
6928
6848
  //which prevents us just changing all instances of Identity that start with $
6929
6849
  const scopeStack = new ScopeStack();
6930
- const stores = new Stores(scopeStack, str, isDeclaration);
6850
+ const stores = new Stores(scopeStack, isDeclaration);
6931
6851
  const scripts = new Scripts(htmlxAst);
6932
6852
  const handleSvelteOptions = (node) => {
6933
6853
  for (let i = 0; i < node.attributes.length; i++) {
@@ -7114,7 +7034,7 @@ function processSvelteTemplate(str, options) {
7114
7034
  const { scriptTag, moduleScriptTag } = scripts.getTopLevelScriptTags();
7115
7035
  scripts.blankOtherScriptTags(str);
7116
7036
  //resolve stores
7117
- const resolvedStores = stores.resolveStores();
7037
+ const resolvedStores = stores.getStoreNames();
7118
7038
  return {
7119
7039
  htmlAst: htmlxAst,
7120
7040
  moduleScriptTag,
@@ -7164,7 +7084,8 @@ function svelte2tsx(svelte, options = {}) {
7164
7084
  if (scriptTag.start != instanceScriptTarget) {
7165
7085
  str.move(scriptTag.start, scriptTag.end, instanceScriptTarget);
7166
7086
  }
7167
- const res = processInstanceScriptContent(str, scriptTag, events, implicitStoreValues, options.mode);
7087
+ const res = processInstanceScriptContent(str, scriptTag, events, implicitStoreValues, options.mode,
7088
+ /**hasModuleScripts */ !!moduleScriptTag);
7168
7089
  uses$$props = uses$$props || res.uses$$props;
7169
7090
  uses$$restProps = uses$$restProps || res.uses$$restProps;
7170
7091
  uses$$slots = uses$$slots || res.uses$$slots;
package/index.mjs CHANGED
@@ -2066,7 +2066,7 @@ const oneWayBindingAttributes$1 = new Map(['clientWidth', 'clientHeight', 'offse
2066
2066
  * List of all binding names that are transformed to sth like `binding = variable`.
2067
2067
  * This applies to readonly bindings and the this binding.
2068
2068
  */
2069
- const assignmentBindings = new Set([...oneWayBindingAttributes$1.keys(), 'this']);
2069
+ new Set([...oneWayBindingAttributes$1.keys(), 'this']);
2070
2070
  /**
2071
2071
  * Transform bind:xxx into something that conforms to JSX
2072
2072
  */
@@ -2410,22 +2410,42 @@ function handleEventHandler$1(htmlx, str, attr, parent) {
2410
2410
  */
2411
2411
  function handleIf$1(htmlx, str, ifBlock, ifScope) {
2412
2412
  const endIf = htmlx.lastIndexOf('{', ifBlock.end - 1);
2413
+ const constTags = extractConstTags(ifBlock.children);
2414
+ const ifConditionEnd = htmlx.indexOf('}', ifBlock.expression.end) + 1;
2415
+ const hasConstTags = !!constTags.length;
2416
+ const endIIFE = createEndIIFE(hasConstTags);
2417
+ const startIIFE = createStartIIFE(hasConstTags);
2418
+ if (hasConstTags) {
2419
+ // {@const hi = exp} <div>{hi}> -> {(() => { const hi = exp; return <> <div>{hi}<div></> })}
2420
+ constTags.forEach((constTag) => {
2421
+ constTag(ifConditionEnd, str);
2422
+ });
2423
+ str.appendRight(ifConditionEnd, 'return <>');
2424
+ if (ifBlock.else) {
2425
+ // {:else} -> </>})()}</> : <>
2426
+ const elseWord = htmlx.lastIndexOf(':else', ifBlock.else.start);
2427
+ const elseStart = htmlx.lastIndexOf('{', elseWord);
2428
+ str.appendLeft(elseStart, endIIFE);
2429
+ }
2430
+ }
2413
2431
  if (ifBlock.elseif) {
2414
2432
  // {:else if expr} -> : (expr) ? <>
2433
+ // {:else if expr}{@const ...} -> : (expr) ? <>{(() => {const ...; return <>
2415
2434
  const elseIfStart = htmlx.lastIndexOf('{', ifBlock.expression.start);
2416
- const elseIfConditionEnd = htmlx.indexOf('}', ifBlock.expression.end) + 1;
2417
- str.overwrite(elseIfStart, ifBlock.expression.start, '</> : (', { contentOnly: true });
2418
- str.overwrite(withTrailingPropertyAccess$1(str.original, ifBlock.expression.end), elseIfConditionEnd, ') ? <>');
2435
+ str.overwrite(elseIfStart, ifBlock.expression.start, '</> : (', {
2436
+ contentOnly: true
2437
+ });
2438
+ str.overwrite(withTrailingPropertyAccess$1(str.original, ifBlock.expression.end), ifConditionEnd, ') ? <>' + startIIFE);
2419
2439
  ifScope.addElseIf(ifBlock.expression, str);
2420
2440
  if (!ifBlock.else) {
2421
- str.appendLeft(endIf, '</> : <>');
2441
+ str.appendLeft(endIf, endIIFE + '</> : <>');
2422
2442
  }
2423
2443
  return;
2424
2444
  }
2425
2445
  // {#if expr} -> {(expr) ? <>
2446
+ // {#if expr}{@const ...} -> {(expr) ? <>{(() => {const ...; return <>
2426
2447
  str.overwrite(ifBlock.start, ifBlock.expression.start, '{(', { contentOnly: true });
2427
- const end = htmlx.indexOf('}', ifBlock.expression.end);
2428
- str.overwrite(withTrailingPropertyAccess$1(str.original, ifBlock.expression.end), end + 1, ') ? <>', { contentOnly: true });
2448
+ str.overwrite(withTrailingPropertyAccess$1(str.original, ifBlock.expression.end), ifConditionEnd, ') ? <>' + startIIFE, { contentOnly: true });
2429
2449
  ifScope.addNestedIf(ifBlock.expression, str);
2430
2450
  if (ifBlock.else) {
2431
2451
  // {/if} -> </> }
@@ -2433,11 +2453,21 @@ function handleIf$1(htmlx, str, ifBlock, ifScope) {
2433
2453
  }
2434
2454
  else {
2435
2455
  // {/if} -> </> : <></>}
2436
- str.overwrite(endIf, ifBlock.end, '</> : <></>}', { contentOnly: true });
2456
+ // {@const ...} -> </>})()}</> : <></>}
2457
+ str.overwrite(endIf, ifBlock.end, endIIFE + '</> : <></>}', {
2458
+ contentOnly: true
2459
+ });
2437
2460
  }
2438
2461
  }
2462
+ function createStartIIFE(hasConstTags) {
2463
+ return hasConstTags ? '{(() => {' : '';
2464
+ }
2465
+ function createEndIIFE(hasConstTags) {
2466
+ return hasConstTags ? '</>})()}' : '';
2467
+ }
2439
2468
  /**
2440
2469
  * {:else} ---> </> : <>
2470
+ * {:else} {@const ...} -> </> : <>{(() => { const ...; return<>
2441
2471
  */
2442
2472
  function handleElse$1(htmlx, str, elseBlock, parent, ifScope) {
2443
2473
  var _a, _b;
@@ -2448,8 +2478,18 @@ function handleElse$1(htmlx, str, elseBlock, parent, ifScope) {
2448
2478
  const elseEnd = htmlx.lastIndexOf('}', elseBlock.start);
2449
2479
  const elseword = htmlx.lastIndexOf(':else', elseEnd);
2450
2480
  const elseStart = htmlx.lastIndexOf('{', elseword);
2451
- str.overwrite(elseStart, elseEnd + 1, '</> : <>');
2481
+ const constTags = extractConstTags(elseBlock.children);
2482
+ const hasConstTags = !!constTags.length;
2483
+ str.overwrite(elseStart, elseEnd + 1, '</> : <>' + createStartIIFE(hasConstTags));
2452
2484
  ifScope.addElse();
2485
+ if (!hasConstTags) {
2486
+ return;
2487
+ }
2488
+ constTags.forEach((constTag) => {
2489
+ constTag(elseEnd + 1, str);
2490
+ });
2491
+ str.appendRight(elseEnd + 1, 'return <>');
2492
+ str.appendLeft(elseBlock.end, createEndIIFE(true));
2453
2493
  }
2454
2494
 
2455
2495
  var IfType;
@@ -3421,7 +3461,6 @@ class Element {
3421
3461
  case 'svelte:head':
3422
3462
  case 'svelte:window':
3423
3463
  case 'svelte:body':
3424
- case 'svelte:element':
3425
3464
  case 'svelte:fragment': {
3426
3465
  // remove the colon: svelte:xxx -> sveltexxx
3427
3466
  const nodeName = `svelte${this.node.name.substring(7)}`;
@@ -3430,6 +3469,18 @@ class Element {
3430
3469
  this.addNameConstDeclaration = () => (this.startTransformation[0] = `{ const ${this._name} = ${createElement}("${nodeName}", {`);
3431
3470
  break;
3432
3471
  }
3472
+ case 'svelte:element': {
3473
+ const nodeName = this.node.tag
3474
+ ? typeof this.node.tag !== 'string'
3475
+ ? [this.node.tag.start, this.node.tag.end]
3476
+ : `"${this.node.tag}"`
3477
+ : '""';
3478
+ this._name = '$$_svelteelement' + this.computeDepth();
3479
+ this.startTransformation.push(`{ ${createElement}(`, nodeName, ', {');
3480
+ this.addNameConstDeclaration = () => ((this.startTransformation[0] = `{ const ${this._name} = ${createElement}(`),
3481
+ ', {');
3482
+ break;
3483
+ }
3433
3484
  case 'slot': {
3434
3485
  // If the element is a <slot> tag, create the element with the createSlot-function
3435
3486
  // which is created inside createRenderFunction.ts to check that the name and attributes
@@ -3884,7 +3935,7 @@ function handleAttribute(str, attr, parent, preserveCase, element) {
3884
3935
  return;
3885
3936
  }
3886
3937
  if (attr.value.length == 0) {
3887
- // attr=""
3938
+ // shouldn't happen
3888
3939
  addAttribute(attributeName, ['""']);
3889
3940
  return;
3890
3941
  }
@@ -3892,6 +3943,11 @@ function handleAttribute(str, attr, parent, preserveCase, element) {
3892
3943
  if (attr.value.length == 1) {
3893
3944
  const attrVal = attr.value[0];
3894
3945
  if (attrVal.type == 'Text') {
3946
+ // Handle the attr="" special case with a transformation that allows mapping of the position
3947
+ if (attrVal.start === attrVal.end) {
3948
+ addAttribute(attributeName, [[attrVal.start - 1, attrVal.end + 1]]);
3949
+ return;
3950
+ }
3895
3951
  const hasBrackets = str.original.lastIndexOf('}', attrVal.end) === attrVal.end - 1 ||
3896
3952
  str.original.lastIndexOf('}"', attrVal.end) === attrVal.end - 1 ||
3897
3953
  str.original.lastIndexOf("}'", attrVal.end) === attrVal.end - 1;
@@ -4772,22 +4828,6 @@ function getNamesFromLabeledStatement(node) {
4772
4828
  // svelte won't let you create a variable with $ prefix (reserved for stores)
4773
4829
  .filter((name) => !name.startsWith('$')));
4774
4830
  }
4775
- function isSafeToPrefixWithSemicolon(node) {
4776
- let parent = node.parent;
4777
- while (parent && !ts__default.isExpressionStatement(parent)) {
4778
- parent = parent.parent;
4779
- }
4780
- if (!parent) {
4781
- return false;
4782
- }
4783
- return (parent.getStart() === node.getStart() &&
4784
- !(parent.parent &&
4785
- (ts__default.isIfStatement(parent.parent) ||
4786
- ts__default.isForStatement(parent.parent) ||
4787
- ts__default.isForInStatement(parent.parent) ||
4788
- ts__default.isForOfStatement(parent.parent) ||
4789
- ts__default.isWhileStatement(parent.parent))));
4790
- }
4791
4831
 
4792
4832
  function is$$EventsDeclaration(node) {
4793
4833
  return isInterfaceOrTypeDeclaration(node) && node.name.text === '$$Events';
@@ -5892,72 +5932,12 @@ function getSingleSlotDef(componentNode, slotName) {
5892
5932
  return `__sveltets_1_instanceOf(${componentType}).$$slot_def['${slotName}']`;
5893
5933
  }
5894
5934
 
5895
- function handleStore(node, parent, str) {
5896
- const storename = node.name.slice(1);
5897
- //handle assign to
5898
- if (parent.type == 'AssignmentExpression' && parent.left == node && parent.operator == '=') {
5899
- const dollar = str.original.indexOf('$', node.start);
5900
- str.remove(dollar, dollar + 1);
5901
- str.overwrite(node.end, str.original.indexOf('=', node.end) + 1, '.set(');
5902
- str.appendLeft(parent.end, ')');
5903
- return;
5904
- }
5905
- // handle Assignment operators ($store +=, -=, *=, /=, %=, **=, etc.)
5906
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Assignment
5907
- const operators = ['+=', '-=', '*=', '/=', '%=', '**=', '<<=', '>>=', '>>>=', '&=', '^=', '|='];
5908
- if (parent.type == 'AssignmentExpression' &&
5909
- parent.left == node &&
5910
- operators.includes(parent.operator)) {
5911
- const storename = node.name.slice(1); // drop the $
5912
- const operator = parent.operator.substring(0, parent.operator.length - 1); // drop the = sign
5913
- str.overwrite(parent.start, str.original.indexOf('=', node.end) + 1, `${storename}.set( $${storename} ${operator}`);
5914
- str.appendLeft(parent.end, ')');
5915
- return;
5916
- }
5917
- // handle $store++, $store--, ++$store, --$store
5918
- if (parent.type == 'UpdateExpression') {
5919
- let simpleOperator;
5920
- if (parent.operator === '++') {
5921
- simpleOperator = '+';
5922
- }
5923
- if (parent.operator === '--') {
5924
- simpleOperator = '-';
5925
- }
5926
- if (simpleOperator) {
5927
- const storename = node.name.slice(1); // drop the $
5928
- str.overwrite(parent.start, parent.end, `(${storename}.set( $${storename} ${simpleOperator} 1), $${storename})`);
5929
- }
5930
- else {
5931
- console.warn(`Warning - unrecognized UpdateExpression operator ${parent.operator}!
5932
- This is an edge case unaccounted for in svelte2tsx, please file an issue:
5933
- https://github.com/sveltejs/language-tools/issues/new/choose
5934
- `, str.original.slice(parent.start, parent.end));
5935
- }
5936
- return;
5937
- }
5938
- const dollar = str.original.indexOf('$', node.start);
5939
- // handle bindings which are transformed to assignments. These need special treatment because
5940
- // `(__sveltets_1_store_get(foo), foo$) = something` is syntactically invalid
5941
- // Therefore remove the outer commas. Note: This relies on the binding expression wrapping
5942
- // this statement with __sveltets_1_empty
5943
- if (parent.type === 'Binding' && assignmentBindings.has(parent.name)) {
5944
- str.overwrite(dollar, dollar + 1, '__sveltets_1_store_get(', { contentOnly: true });
5945
- str.prependLeft(node.end, `), $${storename}`);
5946
- return;
5947
- }
5948
- // we change "$store" references into "(__sveltets_1_store_get(store), $store)"
5949
- // - in order to get ts errors if store is not assignable to SvelteStore
5950
- // - use $store variable defined above to get ts flow control
5951
- str.overwrite(dollar, dollar + 1, '(__sveltets_1_store_get(', { contentOnly: true });
5952
- str.prependLeft(node.end, `), $${storename})`);
5953
- }
5954
5935
  const reservedNames = new Set(['$$props', '$$restProps', '$$slots']);
5955
5936
  class Stores {
5956
- constructor(scope, str, isDeclaration) {
5937
+ constructor(scope, isDeclaration) {
5957
5938
  this.scope = scope;
5958
- this.str = str;
5959
5939
  this.isDeclaration = isDeclaration;
5960
- this.pendingStoreResolutions = [];
5940
+ this.possibleStores = [];
5961
5941
  }
5962
5942
  handleDirective(node, str) {
5963
5943
  if (this.notAStore(node.name) || this.isDeclaration.value) {
@@ -5965,7 +5945,7 @@ class Stores {
5965
5945
  }
5966
5946
  const start = str.original.indexOf('$', node.start);
5967
5947
  const end = start + node.name.length;
5968
- this.pendingStoreResolutions.push({
5948
+ this.possibleStores.push({
5969
5949
  node: { type: 'Identifier', start, end, name: node.name },
5970
5950
  parent: { start: 0, end: 0, type: '' },
5971
5951
  scope: this.scope.current
@@ -5989,18 +5969,17 @@ class Stores {
5989
5969
  if (isObjectKey(parent, prop)) {
5990
5970
  return;
5991
5971
  }
5992
- this.pendingStoreResolutions.push({ node, parent, scope: this.scope.current });
5972
+ this.possibleStores.push({ node, parent, scope: this.scope.current });
5993
5973
  }
5994
5974
  }
5995
- resolveStores() {
5996
- const unresolvedStores = this.pendingStoreResolutions.filter(({ node, scope }) => {
5975
+ getStoreNames() {
5976
+ const stores = this.possibleStores.filter(({ node, scope }) => {
5997
5977
  const name = node.name;
5998
5978
  // if variable starting with '$' was manually declared by the user,
5999
5979
  // this isn't a store access.
6000
5980
  return !scope.hasDefined(name);
6001
5981
  });
6002
- unresolvedStores.forEach(({ node, parent }) => handleStore(node, parent, this.str));
6003
- return unresolvedStores.map(({ node }) => node.name.slice(1));
5982
+ return stores.map(({ node }) => node.name.slice(1));
6004
5983
  }
6005
5984
  notAStore(name) {
6006
5985
  return name[0] !== '$' || reservedNames.has(name);
@@ -6133,12 +6112,15 @@ class ImplicitTopLevelNames {
6133
6112
  // to `let {a} = b;` produces valid code.
6134
6113
  if (ts__default.isExpressionStatement(node.statement) &&
6135
6114
  isParenthesizedObjectOrArrayLiteralExpression(node.statement.expression)) {
6136
- const start = node.statement.expression.getStart() + this.astOffset;
6137
- this.str.overwrite(start, start + 1, '', { contentOnly: true });
6138
- const end = node.statement.expression.getEnd() + this.astOffset - 1;
6115
+ const parenthesizedExpression = node.statement.expression;
6116
+ const parenthesisStart = parenthesizedExpression.getStart() + this.astOffset;
6117
+ const expressionStart = parenthesizedExpression.expression.getStart() + this.astOffset;
6118
+ this.str.overwrite(parenthesisStart, expressionStart, '', { contentOnly: true });
6119
+ const parenthesisEnd = parenthesizedExpression.getEnd() + this.astOffset;
6120
+ const expressionEnd = parenthesizedExpression.expression.getEnd() + this.astOffset;
6139
6121
  // We need to keep the `)` of the "wrap with invalidate" expression above.
6140
6122
  // We overwrite the same range so it's needed.
6141
- overwriteStr(this.str, end, end + 1, ')', true);
6123
+ overwriteStr(this.str, expressionEnd, parenthesisEnd, ')', true);
6142
6124
  }
6143
6125
  }
6144
6126
  }
@@ -6288,9 +6270,15 @@ class Generics {
6288
6270
  /**
6289
6271
  * move imports to top of script so they appear outside our render function
6290
6272
  */
6291
- function handleImportDeclaration(node, str, astOffset, scriptStart) {
6273
+ function handleImportDeclaration(node, str, astOffset, scriptStart, sourceFile) {
6292
6274
  var _a;
6275
+ const scanner = ts__default.createScanner(sourceFile.languageVersion,
6276
+ /*skipTrivia*/ false, sourceFile.languageVariant);
6293
6277
  const comments = (_a = ts__default.getLeadingCommentRanges(node.getFullText(), 0)) !== null && _a !== void 0 ? _a : [];
6278
+ if (!comments.some((comment) => comment.hasTrailingNewLine) &&
6279
+ isNewGroup(sourceFile, node, scanner)) {
6280
+ str.appendRight(node.getStart() + astOffset, '\n');
6281
+ }
6294
6282
  for (const comment of comments) {
6295
6283
  const commentEnd = node.pos + comment.end + astOffset;
6296
6284
  str.move(node.pos + comment.pos + astOffset, commentEnd, scriptStart + 1);
@@ -6303,8 +6291,27 @@ function handleImportDeclaration(node, str, astOffset, scriptStart) {
6303
6291
  const originalEndChar = str.original[node.end + astOffset - 1];
6304
6292
  str.overwrite(node.end + astOffset - 1, node.end + astOffset, originalEndChar + '\n');
6305
6293
  }
6294
+ /**
6295
+ * adopted from https://github.com/microsoft/TypeScript/blob/6e0447fdf165b1cec9fc80802abcc15bd23a268f/src/services/organizeImports.ts#L111
6296
+ */
6297
+ function isNewGroup(sourceFile, topLevelImportDecl, scanner) {
6298
+ const startPos = topLevelImportDecl.getFullStart();
6299
+ const endPos = topLevelImportDecl.getStart();
6300
+ scanner.setText(sourceFile.text, startPos, endPos - startPos);
6301
+ let numberOfNewLines = 0;
6302
+ while (scanner.getTokenPos() < endPos) {
6303
+ const tokenKind = scanner.scan();
6304
+ if (tokenKind === ts__default.SyntaxKind.NewLineTrivia) {
6305
+ numberOfNewLines++;
6306
+ if (numberOfNewLines >= 2) {
6307
+ return true;
6308
+ }
6309
+ }
6310
+ }
6311
+ return false;
6312
+ }
6306
6313
 
6307
- function processInstanceScriptContent(str, script, events, implicitStoreValues, mode) {
6314
+ function processInstanceScriptContent(str, script, events, implicitStoreValues, mode, hasModuleScript) {
6308
6315
  const htmlx = str.original;
6309
6316
  const scriptContent = htmlx.substring(script.content.start, script.content.end);
6310
6317
  const tsAst = ts.createSourceFile('component.ts.svelte', scriptContent, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
@@ -6325,95 +6332,8 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6325
6332
  const rootScope = scope;
6326
6333
  const pushScope = () => (scope = new Scope(scope));
6327
6334
  const popScope = () => (scope = scope.parent);
6328
- const handleStore = (ident, parent) => {
6329
- // ignore "typeof $store"
6330
- if (parent && parent.kind === ts.SyntaxKind.TypeQuery) {
6331
- return;
6332
- }
6333
- // ignore break
6334
- if (parent && parent.kind === ts.SyntaxKind.BreakStatement) {
6335
- return;
6336
- }
6337
- const storename = ident.getText().slice(1); // drop the $
6338
- // handle assign to
6339
- if (parent &&
6340
- ts.isBinaryExpression(parent) &&
6341
- parent.operatorToken.kind == ts.SyntaxKind.EqualsToken &&
6342
- parent.left == ident) {
6343
- //remove $
6344
- const dollar = str.original.indexOf('$', ident.getStart() + astOffset);
6345
- str.remove(dollar, dollar + 1);
6346
- // replace = with .set(
6347
- str.overwrite(ident.end + astOffset, parent.operatorToken.end + astOffset, '.set(');
6348
- // append )
6349
- str.appendLeft(parent.end + astOffset, ')');
6350
- return;
6351
- }
6352
- // handle Assignment operators ($store +=, -=, *=, /=, %=, **=, etc.)
6353
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Assignment
6354
- const operators = {
6355
- [ts.SyntaxKind.PlusEqualsToken]: '+',
6356
- [ts.SyntaxKind.MinusEqualsToken]: '-',
6357
- [ts.SyntaxKind.AsteriskEqualsToken]: '*',
6358
- [ts.SyntaxKind.SlashEqualsToken]: '/',
6359
- [ts.SyntaxKind.PercentEqualsToken]: '%',
6360
- [ts.SyntaxKind.AsteriskAsteriskEqualsToken]: '**',
6361
- [ts.SyntaxKind.LessThanLessThanEqualsToken]: '<<',
6362
- [ts.SyntaxKind.GreaterThanGreaterThanEqualsToken]: '>>',
6363
- [ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken]: '>>>',
6364
- [ts.SyntaxKind.AmpersandEqualsToken]: '&',
6365
- [ts.SyntaxKind.CaretEqualsToken]: '^',
6366
- [ts.SyntaxKind.BarEqualsToken]: '|'
6367
- };
6368
- if (ts.isBinaryExpression(parent) &&
6369
- parent.left == ident &&
6370
- Object.keys(operators).find((x) => x === String(parent.operatorToken.kind))) {
6371
- const operator = operators[parent.operatorToken.kind];
6372
- str.overwrite(parent.getStart() + astOffset, str.original.indexOf('=', ident.end + astOffset) + 1, `${storename}.set( $${storename} ${operator}`);
6373
- str.appendLeft(parent.end + astOffset, ')');
6374
- return;
6375
- }
6376
- // handle $store++, $store--, ++$store, --$store
6377
- if ((ts.isPrefixUnaryExpression(parent) || ts.isPostfixUnaryExpression(parent)) &&
6378
- ![
6379
- ts.SyntaxKind.ExclamationToken,
6380
- ts.SyntaxKind.PlusToken,
6381
- ts.SyntaxKind.MinusToken,
6382
- ts.SyntaxKind.TildeToken // ~$store
6383
- ].includes(parent.operator) /* `!$store` etc does not need processing */) {
6384
- let simpleOperator;
6385
- if (parent.operator === ts.SyntaxKind.PlusPlusToken) {
6386
- simpleOperator = '+';
6387
- }
6388
- if (parent.operator === ts.SyntaxKind.MinusMinusToken) {
6389
- simpleOperator = '-';
6390
- }
6391
- if (simpleOperator) {
6392
- str.overwrite(parent.getStart() + astOffset, parent.end + astOffset, `(${storename}.set( $${storename} ${simpleOperator} 1), $${storename})`);
6393
- return;
6394
- }
6395
- else {
6396
- console.warn(`Warning - unrecognized UnaryExpression operator ${parent.operator}!
6397
- This is an edge case unaccounted for in svelte2tsx, please file an issue:
6398
- https://github.com/sveltejs/language-tools/issues/new/choose
6399
- `, parent.getText());
6400
- }
6401
- }
6402
- // we change "$store" references into "(__sveltets_1_store_get(store), $store)"
6403
- // - in order to get ts errors if store is not assignable to SvelteStore
6404
- // - use $store variable defined above to get ts flow control
6405
- const dollar = str.original.indexOf('$', ident.getStart() + astOffset);
6406
- const getPrefix = isSafeToPrefixWithSemicolon(ident)
6407
- ? ';'
6408
- : ts.isShorthandPropertyAssignment(parent)
6409
- ? // { $store } --> { $store: __sveltets_1_store_get(..)}
6410
- ident.text + ': '
6411
- : '';
6412
- str.overwrite(dollar, dollar + 1, getPrefix + '(__sveltets_1_store_get(');
6413
- str.prependLeft(ident.end + astOffset, `), $${storename})`);
6414
- };
6415
6335
  const resolveStore = (pending) => {
6416
- let { node, parent, scope } = pending;
6336
+ let { node, scope } = pending;
6417
6337
  const name = node.text;
6418
6338
  while (scope) {
6419
6339
  if (scope.declared.has(name)) {
@@ -6422,8 +6342,6 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6422
6342
  }
6423
6343
  scope = scope.parent;
6424
6344
  }
6425
- //We haven't been resolved, we must be a store read/write, handle it.
6426
- handleStore(node, parent);
6427
6345
  const storename = node.getText().slice(1);
6428
6346
  implicitStoreValues.addStoreAcess(storename);
6429
6347
  };
@@ -6500,7 +6418,7 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6500
6418
  exportedNames.handleExportDeclaration(node);
6501
6419
  }
6502
6420
  if (ts.isImportDeclaration(node)) {
6503
- handleImportDeclaration(node, str, astOffset, script.start);
6421
+ handleImportDeclaration(node, str, astOffset, script.start, tsAst);
6504
6422
  // Check if import is the event dispatcher
6505
6423
  events.checkIfImportIsEventDispatcher(node);
6506
6424
  }
@@ -6573,7 +6491,9 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
6573
6491
  .filter(ts.isImportDeclaration)
6574
6492
  .sort((a, b) => a.end - b.end)[0];
6575
6493
  if (firstImport) {
6576
- str.appendRight(firstImport.getStart() + astOffset, '\n');
6494
+ // ensure it's in a newline.
6495
+ // if file has module script ensure an empty line to separate imports
6496
+ str.appendRight(firstImport.getStart() + astOffset, '\n' + (hasModuleScript ? '\n' : ''));
6577
6497
  }
6578
6498
  if (mode === 'dts') {
6579
6499
  // Transform interface declarations to type declarations because indirectly
@@ -6897,7 +6817,7 @@ function processSvelteTemplate(str, options) {
6897
6817
  //track $store variables since we are only supposed to give top level scopes special treatment, and users can declare $blah variables at higher scopes
6898
6818
  //which prevents us just changing all instances of Identity that start with $
6899
6819
  const scopeStack = new ScopeStack();
6900
- const stores = new Stores(scopeStack, str, isDeclaration);
6820
+ const stores = new Stores(scopeStack, isDeclaration);
6901
6821
  const scripts = new Scripts(htmlxAst);
6902
6822
  const handleSvelteOptions = (node) => {
6903
6823
  for (let i = 0; i < node.attributes.length; i++) {
@@ -7084,7 +7004,7 @@ function processSvelteTemplate(str, options) {
7084
7004
  const { scriptTag, moduleScriptTag } = scripts.getTopLevelScriptTags();
7085
7005
  scripts.blankOtherScriptTags(str);
7086
7006
  //resolve stores
7087
- const resolvedStores = stores.resolveStores();
7007
+ const resolvedStores = stores.getStoreNames();
7088
7008
  return {
7089
7009
  htmlAst: htmlxAst,
7090
7010
  moduleScriptTag,
@@ -7134,7 +7054,8 @@ function svelte2tsx(svelte, options = {}) {
7134
7054
  if (scriptTag.start != instanceScriptTarget) {
7135
7055
  str.move(scriptTag.start, scriptTag.end, instanceScriptTarget);
7136
7056
  }
7137
- const res = processInstanceScriptContent(str, scriptTag, events, implicitStoreValues, options.mode);
7057
+ const res = processInstanceScriptContent(str, scriptTag, events, implicitStoreValues, options.mode,
7058
+ /**hasModuleScripts */ !!moduleScriptTag);
7138
7059
  uses$$props = uses$$props || res.uses$$props;
7139
7060
  uses$$restProps = uses$$restProps || res.uses$$restProps;
7140
7061
  uses$$slots = uses$$slots || res.uses$$slots;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte2tsx",
3
- "version": "0.5.8",
3
+ "version": "0.5.11",
4
4
  "description": "Convert Svelte components to TSX for type checking",
5
5
  "author": "David Pershouse",
6
6
  "license": "MIT",
@@ -18,6 +18,7 @@
18
18
  "module": "index.mjs",
19
19
  "types": "index.d.ts",
20
20
  "devDependencies": {
21
+ "@jridgewell/trace-mapping": "^0.3.9",
21
22
  "@rollup/plugin-commonjs": "^15.0.0",
22
23
  "@rollup/plugin-json": "^4.0.0",
23
24
  "@rollup/plugin-node-resolve": "^9.0.0",
@@ -31,13 +32,12 @@
31
32
  "periscopic": "^2.0.2",
32
33
  "rollup": "2.52.7",
33
34
  "rollup-plugin-delete": "^2.0.0",
34
- "source-map": "^0.6.1",
35
35
  "source-map-support": "^0.5.16",
36
36
  "sourcemap-codec": "^1.4.8",
37
- "svelte": "~3.47.0",
37
+ "svelte": "~3.48.0",
38
38
  "tiny-glob": "^0.2.6",
39
39
  "tslib": "^1.10.0",
40
- "typescript": "^4.6.2"
40
+ "typescript": "^4.7.3"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "svelte": "^3.24",
package/svelte-jsx.d.ts CHANGED
@@ -11,11 +11,12 @@ declare namespace svelteHTML {
11
11
  ): SVGElementTagNameMap[K];
12
12
  function mapElementTag(
13
13
  tag: any
14
- ): HTMLElement;
14
+ ): any; // needs to be any because used in context of <svelte:element>
15
15
 
16
16
  function createElement<Elements extends IntrinsicElements, Key extends keyof Elements>(
17
- element: Key, attrs: Elements[Key]
18
- ): Key extends keyof ElementTagNameMap ? ElementTagNameMap[Key] : Key extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[Key] : HTMLElement;
17
+ // "undefined | null" because of <svelte:element>
18
+ element: Key | undefined | null, attrs: Elements[Key]
19
+ ): Key extends keyof ElementTagNameMap ? ElementTagNameMap[Key] : Key extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[Key] : any;
19
20
 
20
21
 
21
22
  type NativeElement = HTMLElement;
@@ -298,7 +299,6 @@ declare namespace svelteHTML {
298
299
  sveltefragment: { slot?: string; };
299
300
  svelteoptions: { [name: string]: any };
300
301
  sveltehead: { [name: string]: any };
301
- svelteelement: { 'this': string | undefined | null; } & HTMLProps<any> & SVGProps<any>;
302
302
 
303
303
  [name: string]: { [name: string]: any };
304
304
  }
@@ -825,6 +825,7 @@ declare namespace svelte.JSX {
825
825
  pattern?: string | undefined | null;
826
826
  placeholder?: string | undefined | null;
827
827
  playsinline?: boolean | undefined | null;
828
+ ping?: string | undefined | null;
828
829
  poster?: string | undefined | null;
829
830
  preload?: string | undefined | null;
830
831
  radiogroup?: string | undefined | null;
package/svelte-shims.d.ts CHANGED
@@ -3,9 +3,11 @@
3
3
  // are loaded and their declarations conflict each other
4
4
  // See https://github.com/sveltejs/language-tools/issues/1059 for an example bug that stems from it
5
5
 
6
+ // -- start svelte-ls-remove --
6
7
  declare module '*.svelte' {
7
8
  export default Svelte2TsxComponent
8
9
  }
10
+ // -- end svelte-ls-remove --
9
11
 
10
12
  declare class Svelte2TsxComponent<
11
13
  Props extends {} = {},
@@ -185,7 +187,7 @@ declare function __sveltets_1_mapElementTag<K extends keyof SVGElementTagNameMap
185
187
  ): SVGElementTagNameMap[K];
186
188
  declare function __sveltets_1_mapElementTag(
187
189
  tag: any
188
- ): HTMLElement;
190
+ ): any; // needs to be any because used in context of <svelte:element>
189
191
 
190
192
  declare function __sveltets_1_bubbleEventDef<Events, K extends keyof Events>(
191
193
  events: Events, eventKey: K