svelte2tsx 0.7.55 → 0.7.57

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 +83 -11
  2. package/index.mjs +83 -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) {
@@ -7468,6 +7520,10 @@ class ExportedNames {
7468
7520
  return;
7469
7521
  }
7470
7522
  name.elements.forEach((child) => {
7523
+ // Skip array binding holes (`[a, , c]`), which have no `name`.
7524
+ if (ts.isOmittedExpression(child)) {
7525
+ return;
7526
+ }
7471
7527
  this.addExportForBindingPattern(child.name, isLet, undefined, type, required);
7472
7528
  });
7473
7529
  }
@@ -8659,8 +8715,8 @@ function svelte2tsx(svelte, options = { parse: compiler.parse }) {
8659
8715
 
8660
8716
  async function emitDts(config) {
8661
8717
  const svelteMap = await createSvelteMap(config);
8662
- const { options, filenames } = loadTsconfig(config, svelteMap);
8663
- const host = await createTsCompilerHost(options, svelteMap);
8718
+ const { options, filenames, absDeclarationDir } = loadTsconfig(config, svelteMap);
8719
+ const host = await createTsCompilerHost(options, svelteMap, absDeclarationDir);
8664
8720
  const program = ts.createProgram(filenames, options, host);
8665
8721
  const result = program.emit();
8666
8722
  const likely_failed_files = result.diagnostics.filter((diagnostic) => {
@@ -8731,6 +8787,9 @@ function loadTsconfig(config, svelteMap) {
8731
8787
  // Add ambient functions so TS knows how to resolve its invocations in the
8732
8788
  // code output of svelte2tsx.
8733
8789
  filenames.push(config.svelteShimsPath);
8790
+ const absDeclarationDir = path__namespace.isAbsolute(config.declarationDir)
8791
+ ? config.declarationDir
8792
+ : path__namespace.resolve(basepath, config.declarationDir);
8734
8793
  return {
8735
8794
  options: {
8736
8795
  ...options,
@@ -8745,10 +8804,11 @@ function loadTsconfig(config, svelteMap) {
8745
8804
  declarationDir: config.declarationDir, // Where to put the declarations
8746
8805
  allowNonTsExtensions: true
8747
8806
  },
8748
- filenames
8807
+ filenames,
8808
+ absDeclarationDir
8749
8809
  };
8750
8810
  }
8751
- async function createTsCompilerHost(options, svelteMap) {
8811
+ async function createTsCompilerHost(options, svelteMap, absDeclarationDir) {
8752
8812
  const host = ts.createCompilerHost(options);
8753
8813
  // TypeScript writes the files relative to the found tsconfig/jsconfig
8754
8814
  // which - at least in the case of the tests - is wrong. Therefore prefix
@@ -8798,7 +8858,19 @@ async function createTsCompilerHost(options, svelteMap) {
8798
8858
  return ts.sys.readDirectory(path, extensionsWithSvelte, exclude, include, depth);
8799
8859
  },
8800
8860
  writeFile(fileName, data, writeByteOrderMark) {
8801
- fileName = pathPrefix ? path__namespace.join(pathPrefix, fileName) : fileName;
8861
+ fileName =
8862
+ pathPrefix && !path__namespace.isAbsolute(fileName)
8863
+ ? path__namespace.join(pathPrefix, fileName)
8864
+ : fileName;
8865
+ // Drop declaration files emitted outside declarationDir.
8866
+ // This happens when TypeScript follows imports to source files outside
8867
+ // rootDir and falls back to emitting declarations next to the source.
8868
+ if (fileName.endsWith('.d.ts') || fileName.endsWith('.d.ts.map')) {
8869
+ const resolved = path__namespace.resolve(fileName).replace(/\\/g, '/');
8870
+ if (!resolved.startsWith(absDeclarationDir.replace(/\\/g, '/') + '/')) {
8871
+ return;
8872
+ }
8873
+ }
8802
8874
  if (fileName.endsWith('d.ts.map')) {
8803
8875
  data = data.replace(/"sources":\["(.+?)"\]/, (_, sourcePath) => {
8804
8876
  // 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) {
@@ -7448,6 +7500,10 @@ class ExportedNames {
7448
7500
  return;
7449
7501
  }
7450
7502
  name.elements.forEach((child) => {
7503
+ // Skip array binding holes (`[a, , c]`), which have no `name`.
7504
+ if (ts.isOmittedExpression(child)) {
7505
+ return;
7506
+ }
7451
7507
  this.addExportForBindingPattern(child.name, isLet, undefined, type, required);
7452
7508
  });
7453
7509
  }
@@ -8639,8 +8695,8 @@ function svelte2tsx(svelte, options = { parse }) {
8639
8695
 
8640
8696
  async function emitDts(config) {
8641
8697
  const svelteMap = await createSvelteMap(config);
8642
- const { options, filenames } = loadTsconfig(config, svelteMap);
8643
- const host = await createTsCompilerHost(options, svelteMap);
8698
+ const { options, filenames, absDeclarationDir } = loadTsconfig(config, svelteMap);
8699
+ const host = await createTsCompilerHost(options, svelteMap, absDeclarationDir);
8644
8700
  const program = ts.createProgram(filenames, options, host);
8645
8701
  const result = program.emit();
8646
8702
  const likely_failed_files = result.diagnostics.filter((diagnostic) => {
@@ -8711,6 +8767,9 @@ function loadTsconfig(config, svelteMap) {
8711
8767
  // Add ambient functions so TS knows how to resolve its invocations in the
8712
8768
  // code output of svelte2tsx.
8713
8769
  filenames.push(config.svelteShimsPath);
8770
+ const absDeclarationDir = path.isAbsolute(config.declarationDir)
8771
+ ? config.declarationDir
8772
+ : path.resolve(basepath, config.declarationDir);
8714
8773
  return {
8715
8774
  options: {
8716
8775
  ...options,
@@ -8725,10 +8784,11 @@ function loadTsconfig(config, svelteMap) {
8725
8784
  declarationDir: config.declarationDir, // Where to put the declarations
8726
8785
  allowNonTsExtensions: true
8727
8786
  },
8728
- filenames
8787
+ filenames,
8788
+ absDeclarationDir
8729
8789
  };
8730
8790
  }
8731
- async function createTsCompilerHost(options, svelteMap) {
8791
+ async function createTsCompilerHost(options, svelteMap, absDeclarationDir) {
8732
8792
  const host = ts.createCompilerHost(options);
8733
8793
  // TypeScript writes the files relative to the found tsconfig/jsconfig
8734
8794
  // which - at least in the case of the tests - is wrong. Therefore prefix
@@ -8778,7 +8838,19 @@ async function createTsCompilerHost(options, svelteMap) {
8778
8838
  return ts.sys.readDirectory(path, extensionsWithSvelte, exclude, include, depth);
8779
8839
  },
8780
8840
  writeFile(fileName, data, writeByteOrderMark) {
8781
- fileName = pathPrefix ? path.join(pathPrefix, fileName) : fileName;
8841
+ fileName =
8842
+ pathPrefix && !path.isAbsolute(fileName)
8843
+ ? path.join(pathPrefix, fileName)
8844
+ : fileName;
8845
+ // Drop declaration files emitted outside declarationDir.
8846
+ // This happens when TypeScript follows imports to source files outside
8847
+ // rootDir and falls back to emitting declarations next to the source.
8848
+ if (fileName.endsWith('.d.ts') || fileName.endsWith('.d.ts.map')) {
8849
+ const resolved = path.resolve(fileName).replace(/\\/g, '/');
8850
+ if (!resolved.startsWith(absDeclarationDir.replace(/\\/g, '/') + '/')) {
8851
+ return;
8852
+ }
8853
+ }
8782
8854
  if (fileName.endsWith('d.ts.map')) {
8783
8855
  data = data.replace(/"sources":\["(.+?)"\]/, (_, sourcePath) => {
8784
8856
  // The inverse of the pathPrefix adjustment
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte2tsx",
3
- "version": "0.7.55",
3
+ "version": "0.7.57",
4
4
  "description": "Convert Svelte components to TSX for type checking",
5
5
  "author": "The Svelte Community",
6
6
  "license": "MIT",
@@ -25,13 +25,13 @@
25
25
  "@rollup/plugin-node-resolve": "^15.0.0",
26
26
  "@rollup/plugin-typescript": "^10.0.0",
27
27
  "@types/estree": "^0.0.42",
28
- "@types/mocha": "^9.1.0",
28
+ "@types/mocha": "^10.0.10",
29
29
  "@types/node": "^18.0.0",
30
30
  "@types/unist": "^2.0.3",
31
31
  "@types/vfile": "^3.0.2",
32
32
  "estree-walker": "^2.0.1",
33
33
  "magic-string": "^0.30.11",
34
- "mocha": "^9.2.0",
34
+ "mocha": "^11.7.6",
35
35
  "periscopic": "^2.0.2",
36
36
  "rollup": "3.7.5",
37
37
  "rollup-plugin-delete": "^2.0.0",