svelte2tsx 0.7.54 → 0.7.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/index.js +79 -11
  2. package/index.mjs +79 -11
  3. package/package.json +3 -3
package/index.js CHANGED
@@ -3112,6 +3112,14 @@ function handleConstTag(str, constTag) {
3112
3112
  str.overwrite(withTrailingPropertyAccess(str.original, constTag.expression.end), constTag.end, ';');
3113
3113
  }
3114
3114
 
3115
+ /**
3116
+ * `{let x = y}` --> `let x = y;`
3117
+ */
3118
+ function handleDeclarationTag(str, declarationTag) {
3119
+ str.remove(declarationTag.start, declarationTag.declaration.start);
3120
+ str.overwrite(declarationTag.declaration.end, declarationTag.end, ';');
3121
+ }
3122
+
3115
3123
  /**
3116
3124
  * {@debug a} ---> ;a;
3117
3125
  * {@debug a, b} ---> ;a;b;
@@ -3641,6 +3649,34 @@ function hoistSnippetBlock(str, blockOrEl) {
3641
3649
  str.move(node.start, node.end, targetPosition);
3642
3650
  }
3643
3651
  }
3652
+ /**
3653
+ * Periscopic's `analyze()` does not treat `<Foo />` template tags as references to `Foo`.
3654
+ * Collect those component names so snippet hoisting does not move snippets that use
3655
+ * instance-scope components into the module script.
3656
+ */
3657
+ function collectSnippetComponentGlobals(body) {
3658
+ const names = new Set();
3659
+ for (const root of body !== null && body !== void 0 ? body : []) {
3660
+ walk(root, {
3661
+ enter(node) {
3662
+ var _a;
3663
+ if (node.type === 'InlineComponent') {
3664
+ if (node.name === 'svelte:component') {
3665
+ if (((_a = node.expression) === null || _a === void 0 ? void 0 : _a.type) === 'Identifier') {
3666
+ names.add(node.expression.name);
3667
+ }
3668
+ }
3669
+ else if (node.name !== 'svelte:self' &&
3670
+ !node.name.startsWith('svelte:') &&
3671
+ /^([A-Z]|([a-zA-Z]+\.))/.test(node.name)) {
3672
+ names.add(node.name);
3673
+ }
3674
+ }
3675
+ }
3676
+ });
3677
+ }
3678
+ return [...names];
3679
+ }
3644
3680
 
3645
3681
  /**
3646
3682
  * `{@render foo(x)}` --> `;foo(x);`
@@ -5175,6 +5211,7 @@ function convertHtmlxToJsx(str, ast, tags, options = { svelte5Plus: false }) {
5175
5211
  const rootSnippets = [];
5176
5212
  let element;
5177
5213
  const pendingSnippetHoistCheck = new Set();
5214
+ let elementBeforeSnippet = [];
5178
5215
  let uses$$props = false;
5179
5216
  let uses$$restProps = false;
5180
5217
  let uses$$slots = false;
@@ -5354,12 +5391,14 @@ function convertHtmlxToJsx(str, ast, tags, options = { svelte5Plus: false }) {
5354
5391
  break;
5355
5392
  case 'SnippetBlock':
5356
5393
  scopeStack.push();
5357
- handleSnippet(str, node, (element instanceof InlineComponent &&
5394
+ const parentComponent = (element instanceof InlineComponent &&
5358
5395
  estreeTypedParent.type === 'InlineComponent') ||
5359
- (element instanceof Element &&
5360
- element.tagName === 'svelte:boundary')
5396
+ (element instanceof Element && element.tagName === 'svelte:boundary')
5361
5397
  ? element
5362
- : undefined, emitJsDoc, isTsFile);
5398
+ : undefined;
5399
+ elementBeforeSnippet.push(element);
5400
+ element = undefined;
5401
+ handleSnippet(str, node, parentComponent, emitJsDoc, isTsFile);
5363
5402
  if (parent === ast) {
5364
5403
  // root snippet -> move to instance script or possibly even module script
5365
5404
  const result = analyze({
@@ -5375,6 +5414,11 @@ function convertHtmlxToJsx(str, ast, tags, options = { svelte5Plus: false }) {
5375
5414
  body: node.children // wrong AST, but periscopic doesn't care
5376
5415
  }
5377
5416
  });
5417
+ for (const name of collectSnippetComponentGlobals(node.children)) {
5418
+ if (!result.globals.has(name)) {
5419
+ result.globals.set(name, { name });
5420
+ }
5421
+ }
5378
5422
  rootSnippets.push([
5379
5423
  node.start,
5380
5424
  node.end,
@@ -5399,6 +5443,9 @@ function convertHtmlxToJsx(str, ast, tags, options = { svelte5Plus: false }) {
5399
5443
  case 'ConstTag':
5400
5444
  handleConstTag(str, node);
5401
5445
  break;
5446
+ case 'DeclarationTag':
5447
+ handleDeclarationTag(str, node);
5448
+ break;
5402
5449
  case 'RenderTag':
5403
5450
  handleRenderTag(str, node);
5404
5451
  break;
@@ -5554,8 +5601,11 @@ function convertHtmlxToJsx(str, ast, tags, options = { svelte5Plus: false }) {
5554
5601
  case 'BlockStatement':
5555
5602
  case 'FunctionDeclaration':
5556
5603
  case 'ArrowFunctionExpression':
5604
+ scopeStack.pop();
5605
+ break;
5557
5606
  case 'SnippetBlock':
5558
5607
  scopeStack.pop();
5608
+ element = elementBeforeSnippet.pop();
5559
5609
  break;
5560
5610
  case 'EachBlock':
5561
5611
  onTemplateScopeLeave();
@@ -7081,6 +7131,7 @@ class ExportedNames {
7081
7131
  }
7082
7132
  handle$propsRune(node) {
7083
7133
  var _a, _b;
7134
+ const bindingLocalNames = [];
7084
7135
  // Check if the $props() rune uses $bindable()
7085
7136
  if (ts.isObjectBindingPattern(node.name)) {
7086
7137
  for (const element of node.name.elements) {
@@ -7100,14 +7151,15 @@ class ExportedNames {
7100
7151
  if (ts.isCallExpression(call) && ts.isIdentifier(call.expression)) {
7101
7152
  if (call.expression.text === '$bindable') {
7102
7153
  this.$props.bindings.push(name);
7154
+ bindingLocalNames.push(element.name.text);
7103
7155
  }
7104
7156
  }
7105
7157
  }
7106
7158
  }
7107
7159
  }
7108
7160
  }
7109
- if (this.$props.bindings.length > 0) {
7110
- this.str.appendLeft(node.end + this.astOffset, surroundWithIgnoreComments(';' + this.$props.bindings.map((prop) => prop + ';').join('')));
7161
+ if (bindingLocalNames.length > 0) {
7162
+ this.str.appendLeft(node.end + this.astOffset, surroundWithIgnoreComments(';' + bindingLocalNames.map((prop) => prop + ';').join('')));
7111
7163
  }
7112
7164
  // Easy mode: User uses TypeScript and typed the $props() rune
7113
7165
  if (((_a = node.initializer.typeArguments) === null || _a === void 0 ? void 0 : _a.length) > 0 || node.type) {
@@ -8659,8 +8711,8 @@ function svelte2tsx(svelte, options = { parse: compiler.parse }) {
8659
8711
 
8660
8712
  async function emitDts(config) {
8661
8713
  const svelteMap = await createSvelteMap(config);
8662
- const { options, filenames } = loadTsconfig(config, svelteMap);
8663
- const host = await createTsCompilerHost(options, svelteMap);
8714
+ const { options, filenames, absDeclarationDir } = loadTsconfig(config, svelteMap);
8715
+ const host = await createTsCompilerHost(options, svelteMap, absDeclarationDir);
8664
8716
  const program = ts.createProgram(filenames, options, host);
8665
8717
  const result = program.emit();
8666
8718
  const likely_failed_files = result.diagnostics.filter((diagnostic) => {
@@ -8731,6 +8783,9 @@ function loadTsconfig(config, svelteMap) {
8731
8783
  // Add ambient functions so TS knows how to resolve its invocations in the
8732
8784
  // code output of svelte2tsx.
8733
8785
  filenames.push(config.svelteShimsPath);
8786
+ const absDeclarationDir = path__namespace.isAbsolute(config.declarationDir)
8787
+ ? config.declarationDir
8788
+ : path__namespace.resolve(basepath, config.declarationDir);
8734
8789
  return {
8735
8790
  options: {
8736
8791
  ...options,
@@ -8745,10 +8800,11 @@ function loadTsconfig(config, svelteMap) {
8745
8800
  declarationDir: config.declarationDir, // Where to put the declarations
8746
8801
  allowNonTsExtensions: true
8747
8802
  },
8748
- filenames
8803
+ filenames,
8804
+ absDeclarationDir
8749
8805
  };
8750
8806
  }
8751
- async function createTsCompilerHost(options, svelteMap) {
8807
+ async function createTsCompilerHost(options, svelteMap, absDeclarationDir) {
8752
8808
  const host = ts.createCompilerHost(options);
8753
8809
  // TypeScript writes the files relative to the found tsconfig/jsconfig
8754
8810
  // which - at least in the case of the tests - is wrong. Therefore prefix
@@ -8798,7 +8854,19 @@ async function createTsCompilerHost(options, svelteMap) {
8798
8854
  return ts.sys.readDirectory(path, extensionsWithSvelte, exclude, include, depth);
8799
8855
  },
8800
8856
  writeFile(fileName, data, writeByteOrderMark) {
8801
- fileName = pathPrefix ? path__namespace.join(pathPrefix, fileName) : fileName;
8857
+ fileName =
8858
+ pathPrefix && !path__namespace.isAbsolute(fileName)
8859
+ ? path__namespace.join(pathPrefix, fileName)
8860
+ : fileName;
8861
+ // Drop declaration files emitted outside declarationDir.
8862
+ // This happens when TypeScript follows imports to source files outside
8863
+ // rootDir and falls back to emitting declarations next to the source.
8864
+ if (fileName.endsWith('.d.ts') || fileName.endsWith('.d.ts.map')) {
8865
+ const resolved = path__namespace.resolve(fileName).replace(/\\/g, '/');
8866
+ if (!resolved.startsWith(absDeclarationDir.replace(/\\/g, '/') + '/')) {
8867
+ return;
8868
+ }
8869
+ }
8802
8870
  if (fileName.endsWith('d.ts.map')) {
8803
8871
  data = data.replace(/"sources":\["(.+?)"\]/, (_, sourcePath) => {
8804
8872
  // The inverse of the pathPrefix adjustment
package/index.mjs CHANGED
@@ -3092,6 +3092,14 @@ function handleConstTag(str, constTag) {
3092
3092
  str.overwrite(withTrailingPropertyAccess(str.original, constTag.expression.end), constTag.end, ';');
3093
3093
  }
3094
3094
 
3095
+ /**
3096
+ * `{let x = y}` --> `let x = y;`
3097
+ */
3098
+ function handleDeclarationTag(str, declarationTag) {
3099
+ str.remove(declarationTag.start, declarationTag.declaration.start);
3100
+ str.overwrite(declarationTag.declaration.end, declarationTag.end, ';');
3101
+ }
3102
+
3095
3103
  /**
3096
3104
  * {@debug a} ---> ;a;
3097
3105
  * {@debug a, b} ---> ;a;b;
@@ -3621,6 +3629,34 @@ function hoistSnippetBlock(str, blockOrEl) {
3621
3629
  str.move(node.start, node.end, targetPosition);
3622
3630
  }
3623
3631
  }
3632
+ /**
3633
+ * Periscopic's `analyze()` does not treat `<Foo />` template tags as references to `Foo`.
3634
+ * Collect those component names so snippet hoisting does not move snippets that use
3635
+ * instance-scope components into the module script.
3636
+ */
3637
+ function collectSnippetComponentGlobals(body) {
3638
+ const names = new Set();
3639
+ for (const root of body !== null && body !== void 0 ? body : []) {
3640
+ walk(root, {
3641
+ enter(node) {
3642
+ var _a;
3643
+ if (node.type === 'InlineComponent') {
3644
+ if (node.name === 'svelte:component') {
3645
+ if (((_a = node.expression) === null || _a === void 0 ? void 0 : _a.type) === 'Identifier') {
3646
+ names.add(node.expression.name);
3647
+ }
3648
+ }
3649
+ else if (node.name !== 'svelte:self' &&
3650
+ !node.name.startsWith('svelte:') &&
3651
+ /^([A-Z]|([a-zA-Z]+\.))/.test(node.name)) {
3652
+ names.add(node.name);
3653
+ }
3654
+ }
3655
+ }
3656
+ });
3657
+ }
3658
+ return [...names];
3659
+ }
3624
3660
 
3625
3661
  /**
3626
3662
  * `{@render foo(x)}` --> `;foo(x);`
@@ -5155,6 +5191,7 @@ function convertHtmlxToJsx(str, ast, tags, options = { svelte5Plus: false }) {
5155
5191
  const rootSnippets = [];
5156
5192
  let element;
5157
5193
  const pendingSnippetHoistCheck = new Set();
5194
+ let elementBeforeSnippet = [];
5158
5195
  let uses$$props = false;
5159
5196
  let uses$$restProps = false;
5160
5197
  let uses$$slots = false;
@@ -5334,12 +5371,14 @@ function convertHtmlxToJsx(str, ast, tags, options = { svelte5Plus: false }) {
5334
5371
  break;
5335
5372
  case 'SnippetBlock':
5336
5373
  scopeStack.push();
5337
- handleSnippet(str, node, (element instanceof InlineComponent &&
5374
+ const parentComponent = (element instanceof InlineComponent &&
5338
5375
  estreeTypedParent.type === 'InlineComponent') ||
5339
- (element instanceof Element &&
5340
- element.tagName === 'svelte:boundary')
5376
+ (element instanceof Element && element.tagName === 'svelte:boundary')
5341
5377
  ? element
5342
- : undefined, emitJsDoc, isTsFile);
5378
+ : undefined;
5379
+ elementBeforeSnippet.push(element);
5380
+ element = undefined;
5381
+ handleSnippet(str, node, parentComponent, emitJsDoc, isTsFile);
5343
5382
  if (parent === ast) {
5344
5383
  // root snippet -> move to instance script or possibly even module script
5345
5384
  const result = analyze({
@@ -5355,6 +5394,11 @@ function convertHtmlxToJsx(str, ast, tags, options = { svelte5Plus: false }) {
5355
5394
  body: node.children // wrong AST, but periscopic doesn't care
5356
5395
  }
5357
5396
  });
5397
+ for (const name of collectSnippetComponentGlobals(node.children)) {
5398
+ if (!result.globals.has(name)) {
5399
+ result.globals.set(name, { name });
5400
+ }
5401
+ }
5358
5402
  rootSnippets.push([
5359
5403
  node.start,
5360
5404
  node.end,
@@ -5379,6 +5423,9 @@ function convertHtmlxToJsx(str, ast, tags, options = { svelte5Plus: false }) {
5379
5423
  case 'ConstTag':
5380
5424
  handleConstTag(str, node);
5381
5425
  break;
5426
+ case 'DeclarationTag':
5427
+ handleDeclarationTag(str, node);
5428
+ break;
5382
5429
  case 'RenderTag':
5383
5430
  handleRenderTag(str, node);
5384
5431
  break;
@@ -5534,8 +5581,11 @@ function convertHtmlxToJsx(str, ast, tags, options = { svelte5Plus: false }) {
5534
5581
  case 'BlockStatement':
5535
5582
  case 'FunctionDeclaration':
5536
5583
  case 'ArrowFunctionExpression':
5584
+ scopeStack.pop();
5585
+ break;
5537
5586
  case 'SnippetBlock':
5538
5587
  scopeStack.pop();
5588
+ element = elementBeforeSnippet.pop();
5539
5589
  break;
5540
5590
  case 'EachBlock':
5541
5591
  onTemplateScopeLeave();
@@ -7061,6 +7111,7 @@ class ExportedNames {
7061
7111
  }
7062
7112
  handle$propsRune(node) {
7063
7113
  var _a, _b;
7114
+ const bindingLocalNames = [];
7064
7115
  // Check if the $props() rune uses $bindable()
7065
7116
  if (ts.isObjectBindingPattern(node.name)) {
7066
7117
  for (const element of node.name.elements) {
@@ -7080,14 +7131,15 @@ class ExportedNames {
7080
7131
  if (ts.isCallExpression(call) && ts.isIdentifier(call.expression)) {
7081
7132
  if (call.expression.text === '$bindable') {
7082
7133
  this.$props.bindings.push(name);
7134
+ bindingLocalNames.push(element.name.text);
7083
7135
  }
7084
7136
  }
7085
7137
  }
7086
7138
  }
7087
7139
  }
7088
7140
  }
7089
- if (this.$props.bindings.length > 0) {
7090
- this.str.appendLeft(node.end + this.astOffset, surroundWithIgnoreComments(';' + this.$props.bindings.map((prop) => prop + ';').join('')));
7141
+ if (bindingLocalNames.length > 0) {
7142
+ this.str.appendLeft(node.end + this.astOffset, surroundWithIgnoreComments(';' + bindingLocalNames.map((prop) => prop + ';').join('')));
7091
7143
  }
7092
7144
  // Easy mode: User uses TypeScript and typed the $props() rune
7093
7145
  if (((_a = node.initializer.typeArguments) === null || _a === void 0 ? void 0 : _a.length) > 0 || node.type) {
@@ -8639,8 +8691,8 @@ function svelte2tsx(svelte, options = { parse }) {
8639
8691
 
8640
8692
  async function emitDts(config) {
8641
8693
  const svelteMap = await createSvelteMap(config);
8642
- const { options, filenames } = loadTsconfig(config, svelteMap);
8643
- const host = await createTsCompilerHost(options, svelteMap);
8694
+ const { options, filenames, absDeclarationDir } = loadTsconfig(config, svelteMap);
8695
+ const host = await createTsCompilerHost(options, svelteMap, absDeclarationDir);
8644
8696
  const program = ts.createProgram(filenames, options, host);
8645
8697
  const result = program.emit();
8646
8698
  const likely_failed_files = result.diagnostics.filter((diagnostic) => {
@@ -8711,6 +8763,9 @@ function loadTsconfig(config, svelteMap) {
8711
8763
  // Add ambient functions so TS knows how to resolve its invocations in the
8712
8764
  // code output of svelte2tsx.
8713
8765
  filenames.push(config.svelteShimsPath);
8766
+ const absDeclarationDir = path.isAbsolute(config.declarationDir)
8767
+ ? config.declarationDir
8768
+ : path.resolve(basepath, config.declarationDir);
8714
8769
  return {
8715
8770
  options: {
8716
8771
  ...options,
@@ -8725,10 +8780,11 @@ function loadTsconfig(config, svelteMap) {
8725
8780
  declarationDir: config.declarationDir, // Where to put the declarations
8726
8781
  allowNonTsExtensions: true
8727
8782
  },
8728
- filenames
8783
+ filenames,
8784
+ absDeclarationDir
8729
8785
  };
8730
8786
  }
8731
- async function createTsCompilerHost(options, svelteMap) {
8787
+ async function createTsCompilerHost(options, svelteMap, absDeclarationDir) {
8732
8788
  const host = ts.createCompilerHost(options);
8733
8789
  // TypeScript writes the files relative to the found tsconfig/jsconfig
8734
8790
  // which - at least in the case of the tests - is wrong. Therefore prefix
@@ -8778,7 +8834,19 @@ async function createTsCompilerHost(options, svelteMap) {
8778
8834
  return ts.sys.readDirectory(path, extensionsWithSvelte, exclude, include, depth);
8779
8835
  },
8780
8836
  writeFile(fileName, data, writeByteOrderMark) {
8781
- fileName = pathPrefix ? path.join(pathPrefix, fileName) : fileName;
8837
+ fileName =
8838
+ pathPrefix && !path.isAbsolute(fileName)
8839
+ ? path.join(pathPrefix, fileName)
8840
+ : fileName;
8841
+ // Drop declaration files emitted outside declarationDir.
8842
+ // This happens when TypeScript follows imports to source files outside
8843
+ // rootDir and falls back to emitting declarations next to the source.
8844
+ if (fileName.endsWith('.d.ts') || fileName.endsWith('.d.ts.map')) {
8845
+ const resolved = path.resolve(fileName).replace(/\\/g, '/');
8846
+ if (!resolved.startsWith(absDeclarationDir.replace(/\\/g, '/') + '/')) {
8847
+ return;
8848
+ }
8849
+ }
8782
8850
  if (fileName.endsWith('d.ts.map')) {
8783
8851
  data = data.replace(/"sources":\["(.+?)"\]/, (_, sourcePath) => {
8784
8852
  // The inverse of the pathPrefix adjustment
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte2tsx",
3
- "version": "0.7.54",
3
+ "version": "0.7.56",
4
4
  "description": "Convert Svelte components to TSX for type checking",
5
5
  "author": "The Svelte Community",
6
6
  "license": "MIT",
@@ -39,11 +39,11 @@
39
39
  "svelte": "~4.2.19",
40
40
  "tiny-glob": "^0.2.6",
41
41
  "tslib": "^2.4.0",
42
- "typescript": "^5.9.2"
42
+ "typescript": "^6.0.3"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "svelte": "^3.55 || ^4.0.0-next.0 || ^4.0 || ^5.0.0-next.0",
46
- "typescript": "^4.9.4 || ^5.0.0"
46
+ "typescript": "^4.9.4 || ^5.0.0 || ^6.0.0"
47
47
  },
48
48
  "files": [
49
49
  "index.mjs",