svelte2tsx 0.7.48 → 0.7.50

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 (4) hide show
  1. package/index.d.ts +12 -1
  2. package/index.js +299 -42
  3. package/index.mjs +299 -42
  4. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -90,6 +90,11 @@ export function svelte2tsx(
90
90
  * valid JS that tsc can process without errors.
91
91
  */
92
92
  emitJsDoc?: boolean;
93
+ /**
94
+ * Rewrites relative imports that resolve outside the workspace so they stay valid
95
+ * from the generated file location.
96
+ */
97
+ rewriteExternalImports?: InternalHelpers.RewriteExternalImportsConfig;
93
98
  }
94
99
  ): SvelteCompiledToTsx
95
100
 
@@ -163,7 +168,8 @@ export const internalHelpers: {
163
168
  fileName: string,
164
169
  kitFilesSettings: InternalHelpers.KitFilesSettings,
165
170
  getSource: () => ts.SourceFile | undefined,
166
- surround?: (code: string) => string
171
+ surround?: (code: string) => string,
172
+ rewriteExternalImports?: InternalHelpers.RewriteExternalImportsConfig
167
173
  ) => { text: string; addedCode: InternalHelpers.AddedCode[] } | undefined,
168
174
  toVirtualPos: (pos: number, addedCode: InternalHelpers.AddedCode[]) => number,
169
175
  toOriginalPos: (pos: number, addedCode: InternalHelpers.AddedCode[]) => {pos: number; inGenerated: boolean},
@@ -201,4 +207,9 @@ export namespace InternalHelpers {
201
207
  universalHooksPath: string;
202
208
  paramsPath: string;
203
209
  }
210
+
211
+ export interface RewriteExternalImportsConfig {
212
+ workspacePath: string;
213
+ generatedPath: string;
214
+ }
204
215
  }
package/index.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  var dedent = require('dedent-js');
4
4
  var ts = require('typescript');
5
- var scule = require('scule');
6
5
  var path = require('path');
6
+ var scule = require('scule');
7
7
  var compiler = require('svelte/compiler');
8
8
 
9
9
  function _interopNamespaceDefault(e) {
@@ -4897,6 +4897,109 @@ function handleAttachTag(tag, element) {
4897
4897
  }
4898
4898
  }
4899
4899
 
4900
+ function toPosixPath(value) {
4901
+ return value.replace(/\\/g, '/');
4902
+ }
4903
+ function isWithinDirectory(filePath, directoryPath) {
4904
+ const relative = path.relative(path.resolve(directoryPath), path.resolve(filePath));
4905
+ return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
4906
+ }
4907
+ function splitImportSpecifier(specifier) {
4908
+ const queryIndex = specifier.indexOf('?');
4909
+ const hashIndex = specifier.indexOf('#');
4910
+ const cutIndex = queryIndex === -1
4911
+ ? hashIndex
4912
+ : hashIndex === -1
4913
+ ? queryIndex
4914
+ : Math.min(queryIndex, hashIndex);
4915
+ if (cutIndex === -1) {
4916
+ return { pathPart: specifier, suffix: '' };
4917
+ }
4918
+ return {
4919
+ pathPart: specifier.slice(0, cutIndex),
4920
+ suffix: specifier.slice(cutIndex)
4921
+ };
4922
+ }
4923
+ function getExternalImportRewrite(specifier, options) {
4924
+ const sourceDir = path.dirname(options.sourcePath);
4925
+ const generatedDir = path.dirname(options.generatedPath);
4926
+ const { pathPart, suffix } = splitImportSpecifier(specifier);
4927
+ if (!pathPart.startsWith('../')) {
4928
+ return null;
4929
+ }
4930
+ const targetPath = path.resolve(sourceDir, pathPart);
4931
+ if (isWithinDirectory(targetPath, options.workspacePath)) {
4932
+ return null;
4933
+ }
4934
+ const rewrittenRelative = toPosixPath(path.relative(generatedDir, targetPath));
4935
+ const rewritten = `${rewrittenRelative}${suffix}`;
4936
+ if (rewritten === specifier) {
4937
+ return null;
4938
+ }
4939
+ return {
4940
+ rewritten,
4941
+ insertedPrefix: rewrittenRelative.slice(0, rewrittenRelative.length - pathPart.length)
4942
+ };
4943
+ }
4944
+ function getImportTypeSpecifierLiteral(ts_impl, node) {
4945
+ const argument = node.argument;
4946
+ if (ts_impl.isLiteralTypeNode(argument) && ts_impl.isStringLiteralLike(argument.literal)) {
4947
+ return argument.literal;
4948
+ }
4949
+ return undefined;
4950
+ }
4951
+ function rewriteImportTypesInNode(ts_impl, node, applyImportRewrite) {
4952
+ if (ts_impl.isImportTypeNode(node)) {
4953
+ const specifier = getImportTypeSpecifierLiteral(ts_impl, node);
4954
+ if (specifier) {
4955
+ applyImportRewrite(specifier);
4956
+ }
4957
+ }
4958
+ ts_impl.forEachChild(node, (child) => rewriteImportTypesInNode(ts_impl, child, applyImportRewrite));
4959
+ }
4960
+ function rewriteExternalImportsInNode(ts_impl, node, options, on_rewrite) {
4961
+ const applyImportRewrite = (module_specifier) => {
4962
+ const rewrite = getExternalImportRewrite(module_specifier.text, options);
4963
+ if (rewrite) {
4964
+ on_rewrite(module_specifier, rewrite);
4965
+ }
4966
+ };
4967
+ if (ts_impl.isImportDeclaration(node) || ts_impl.isExportDeclaration(node)) {
4968
+ if (node.moduleSpecifier && ts_impl.isStringLiteralLike(node.moduleSpecifier)) {
4969
+ applyImportRewrite(node.moduleSpecifier);
4970
+ }
4971
+ }
4972
+ else if (ts_impl.isCallExpression(node)) {
4973
+ const firstArg = node.arguments[0];
4974
+ if (firstArg && ts_impl.isStringLiteralLike(firstArg)) {
4975
+ const isDynamicImport = node.expression.kind === ts_impl.SyntaxKind.ImportKeyword;
4976
+ const isRequireCall = ts_impl.isIdentifier(node.expression) && node.expression.text === 'require';
4977
+ if (isDynamicImport || isRequireCall) {
4978
+ applyImportRewrite(firstArg);
4979
+ }
4980
+ }
4981
+ }
4982
+ else if (ts_impl.isImportTypeNode(node)) {
4983
+ const specifier = getImportTypeSpecifierLiteral(ts_impl, node);
4984
+ if (specifier) {
4985
+ applyImportRewrite(specifier);
4986
+ }
4987
+ }
4988
+ const jsDoc = node.jsDoc;
4989
+ if (jsDoc) {
4990
+ for (const doc of jsDoc) {
4991
+ rewriteImportTypesInNode(ts_impl, doc, applyImportRewrite);
4992
+ }
4993
+ }
4994
+ }
4995
+ function forEachExternalImportRewrite(ts_impl, source, options, on_rewrite) {
4996
+ const visit = (node) => {
4997
+ rewriteExternalImportsInNode(ts_impl, node, options, on_rewrite);
4998
+ ts_impl.forEachChild(node, visit);
4999
+ };
5000
+ ts_impl.forEachChild(source, visit);
5001
+ }
5002
+
4900
5003
  /**
4901
5004
  * Walks the HTMLx part of the Svelte component
4902
5005
  * and converts it to JSX
@@ -4968,6 +5071,45 @@ function convertHtmlxToJsx(str, ast, tags, options = { svelte5Plus: false }) {
4968
5071
  const handleStyleTag = (node) => {
4969
5072
  str.remove(node.start, node.end);
4970
5073
  };
5074
+ const rewriteImportSpecifier = (importSpecifier) => {
5075
+ var _a;
5076
+ if (!options.rewriteExternalImports) {
5077
+ return importSpecifier;
5078
+ }
5079
+ const rewrite = getExternalImportRewrite(importSpecifier, options.rewriteExternalImports);
5080
+ return (_a = rewrite === null || rewrite === void 0 ? void 0 : rewrite.rewritten) !== null && _a !== void 0 ? _a : importSpecifier;
5081
+ };
5082
+ const rewriteImportsInComment = (comment) => {
5083
+ if (!options.rewriteExternalImports) {
5084
+ return;
5085
+ }
5086
+ const original = str.original.slice(comment.start, comment.end);
5087
+ const rewritten = original.replace(/import\(\s*(['"])([^'"]+)\1\s*\)/g, (fullMatch, quote, importSpecifier) => {
5088
+ const specifier = rewriteImportSpecifier(importSpecifier);
5089
+ return specifier === importSpecifier
5090
+ ? fullMatch
5091
+ : `import(${quote}${specifier}${quote})`;
5092
+ });
5093
+ if (rewritten !== original) {
5094
+ str.overwrite(comment.start, comment.end, rewritten);
5095
+ }
5096
+ };
5097
+ const rewriteNodeCommentImports = (node) => {
5098
+ var _a, _b, _c;
5099
+ if (!options.rewriteExternalImports) {
5100
+ return;
5101
+ }
5102
+ const nodeWithcomments = node;
5103
+ for (const comment of (_a = nodeWithcomments.leadingComments) !== null && _a !== void 0 ? _a : []) {
5104
+ rewriteImportsInComment(comment);
5105
+ }
5106
+ for (const comment of (_b = nodeWithcomments.trailingComments) !== null && _b !== void 0 ? _b : []) {
5107
+ rewriteImportsInComment(comment);
5108
+ }
5109
+ for (const comment of (_c = nodeWithcomments.innerComments) !== null && _c !== void 0 ? _c : []) {
5110
+ rewriteImportsInComment(comment);
5111
+ }
5112
+ };
4971
5113
  const slotHandler = new SlotHandler(str.original);
4972
5114
  let templateScope = new TemplateScope();
4973
5115
  const handleComponentLet = (component) => {
@@ -5008,12 +5150,25 @@ function convertHtmlxToJsx(str, ast, tags, options = { svelte5Plus: false }) {
5008
5150
  isDeclaration.value = true;
5009
5151
  }
5010
5152
  try {
5153
+ rewriteNodeCommentImports(node);
5011
5154
  switch (node.type) {
5012
5155
  case 'Identifier':
5013
5156
  handleIdentifier(node);
5014
5157
  stores.handleIdentifier(node, parent, prop);
5015
5158
  eventHandler.handleIdentifier(node, parent, prop);
5016
5159
  break;
5160
+ case 'ImportExpression': {
5161
+ const source = node.source;
5162
+ if (options.rewriteExternalImports &&
5163
+ (source === null || source === void 0 ? void 0 : source.type) === 'Literal' &&
5164
+ typeof source.value === 'string') {
5165
+ const rewrite = getExternalImportRewrite(source.value, options.rewriteExternalImports);
5166
+ if (rewrite) {
5167
+ str.overwrite(source.start + 1, source.end - 1, rewrite.rewritten);
5168
+ }
5169
+ }
5170
+ break;
5171
+ }
5017
5172
  case 'IfBlock':
5018
5173
  handleIf(str, node);
5019
5174
  break;
@@ -5375,6 +5530,8 @@ function findExports(ts, source, isTsFile) {
5375
5530
  const declaration = statement.declarationList.declarations[0];
5376
5531
  const hasTypeDefinition = !!declaration.type ||
5377
5532
  (!isTsFile && !!ts.getJSDocType(declaration)) ||
5533
+ (!isTsFile &&
5534
+ ts.getJSDocTags(declaration).some((t) => t.tagName.text === 'satisfies')) ||
5378
5535
  (!!declaration.initializer && ts.isSatisfiesExpression(declaration.initializer));
5379
5536
  if (declaration.initializer &&
5380
5537
  (ts.isFunctionExpression(declaration.initializer) ||
@@ -5459,7 +5616,7 @@ function isParamsFile(fileName, basename, paramsPath) {
5459
5616
  !basename.includes('.test') &&
5460
5617
  !basename.includes('.spec'));
5461
5618
  }
5462
- function upsertKitFile(ts, fileName, kitFilesSettings, getSource, surround = (text) => text) {
5619
+ function upsertKitFile(ts, fileName, kitFilesSettings, getSource, surround = (text) => text, rewriteExternalImports) {
5463
5620
  var _a, _b, _c, _d;
5464
5621
  let basename = path.basename(fileName);
5465
5622
  const result = (_d = (_c = (_b = (_a = upsertKitRouteFile(ts, basename, getSource, surround)) !== null && _a !== void 0 ? _a : upsertKitServerHooksFile(ts, fileName, basename, kitFilesSettings.serverHooksPath, getSource, surround)) !== null && _b !== void 0 ? _b : upsertKitClientHooksFile(ts, fileName, basename, kitFilesSettings.clientHooksPath, getSource, surround)) !== null && _c !== void 0 ? _c : upsertKitUniversalHooksFile(ts, fileName, basename, kitFilesSettings.universalHooksPath, getSource, surround)) !== null && _d !== void 0 ? _d : upsertKitParamsFile(ts, fileName, basename, kitFilesSettings.paramsPath, getSource, surround);
@@ -5475,8 +5632,34 @@ function upsertKitFile(ts, fileName, kitFilesSettings, getSource, surround = (te
5475
5632
  pos = added.originalPos;
5476
5633
  }
5477
5634
  text += originalText.slice(pos);
5635
+ if (rewriteExternalImports) {
5636
+ const source = getSource();
5637
+ if (source) {
5638
+ const rewriteOptions = {
5639
+ sourcePath: fileName,
5640
+ generatedPath: rewriteExternalImports.generatedPath,
5641
+ workspacePath: rewriteExternalImports.workspacePath
5642
+ };
5643
+ applyExternalImportRewritesToAddedCode(ts, source, addedCode, rewriteOptions);
5644
+ let rewritePos = 0;
5645
+ text = '';
5646
+ for (const added of addedCode) {
5647
+ text += originalText.slice(rewritePos, added.originalPos) + added.inserted;
5648
+ rewritePos = added.originalPos;
5649
+ }
5650
+ text += originalText.slice(rewritePos);
5651
+ }
5652
+ }
5478
5653
  return { text, addedCode };
5479
5654
  }
5655
+ function applyExternalImportRewritesToAddedCode(ts_impl, source, addedCode, rewriteOptions) {
5656
+ forEachExternalImportRewrite(ts_impl, source, rewriteOptions, (module_specifier, rewrite) => {
5657
+ if (!rewrite.insertedPrefix) {
5658
+ return;
5659
+ }
5660
+ insertCode(addedCode, module_specifier.getStart(source) + 1, rewrite.insertedPrefix);
5661
+ });
5662
+ }
5480
5663
  function upsertKitRouteFile(ts, basename, getSource, surround) {
5481
5664
  if (!isKitRouteFile(basename))
5482
5665
  return;
@@ -5492,15 +5675,27 @@ function upsertKitRouteFile(ts, basename, getSource, surround) {
5492
5675
  // add type to load function if not explicitly typed
5493
5676
  const load = exports.get('load');
5494
5677
  if ((load === null || load === void 0 ? void 0 : load.type) === 'function' && load.node.parameters.length === 1 && !load.hasTypeDefinition) {
5495
- const pos = load.node.parameters[0].getEnd();
5496
- const inserted = surround(`: import('./$types.js').${basename.includes('layout') ? 'Layout' : 'Page'}${basename.includes('server') ? 'Server' : ''}LoadEvent`);
5497
- insert(pos, inserted);
5678
+ const load_type = `import('./$types.js').${basename.includes('layout') ? 'Layout' : 'Page'}${basename.includes('server') ? 'Server' : ''}Load`;
5679
+ if (isTsFile) {
5680
+ const pos = load.node.parameters[0].getEnd();
5681
+ const inserted = surround(`: ${load_type}Event`);
5682
+ insert(pos, inserted);
5683
+ }
5684
+ else {
5685
+ addJsDocParamToFunction(surround, insert, ts, load.node, `${load_type}Event`);
5686
+ }
5498
5687
  }
5499
5688
  else if ((load === null || load === void 0 ? void 0 : load.type) === 'var' && !load.hasTypeDefinition) {
5500
- // "const load = ..." will be transformed into
5501
- // "const load = (...) satisfies PageLoad"
5502
- insert(load.node.initializer.getStart(), surround('('));
5503
- insert(load.node.initializer.getEnd(), surround(`) satisfies import('./$types.js').${basename.includes('layout') ? 'Layout' : 'Page'}${basename.includes('server') ? 'Server' : ''}Load`));
5689
+ const load_type = `import('./$types.js').${basename.includes('layout') ? 'Layout' : 'Page'}${basename.includes('server') ? 'Server' : ''}Load`;
5690
+ if (isTsFile) {
5691
+ // "const load = ..." will be transformed into
5692
+ // "const load = (...) satisfies PageLoad"
5693
+ insert(load.node.initializer.getStart(), surround('('));
5694
+ insert(load.node.initializer.getEnd(), surround(`) satisfies ${load_type}`));
5695
+ }
5696
+ else {
5697
+ addJsDocSatisfiesToVariable(surround, insert, load.node, load_type);
5698
+ }
5504
5699
  }
5505
5700
  // add type to entries function if not explicitly typed
5506
5701
  const entries = exports.get('entries');
@@ -5508,28 +5703,36 @@ function upsertKitRouteFile(ts, basename, getSource, surround) {
5508
5703
  entries.node.parameters.length === 0 &&
5509
5704
  !entries.hasTypeDefinition &&
5510
5705
  !basename.includes('layout')) {
5511
- if (!entries.node.type && entries.node.body) {
5706
+ if (isTsFile && !entries.node.type && entries.node.body) {
5512
5707
  const returnPos = ts.isArrowFunction(entries.node)
5513
5708
  ? entries.node.equalsGreaterThanToken.getStart()
5514
5709
  : entries.node.body.getStart();
5515
5710
  const returnInsertion = surround(`: ReturnType<import('./$types.js').EntryGenerator> `);
5516
5711
  insert(returnPos, returnInsertion);
5517
5712
  }
5713
+ else if (!isTsFile) {
5714
+ addJsDocTypeToFunction(surround, insert, entries.node, `import('./$types.js').EntryGenerator`);
5715
+ }
5518
5716
  }
5519
5717
  // add type to actions variable if not explicitly typed
5520
5718
  const actions = exports.get('actions');
5521
5719
  if ((actions === null || actions === void 0 ? void 0 : actions.type) === 'var' && !actions.hasTypeDefinition && actions.node.initializer) {
5522
- const pos = actions.node.initializer.getEnd();
5523
- const inserted = surround(` satisfies import('./$types.js').Actions`);
5524
- insert(pos, inserted);
5525
- }
5526
- addTypeToVariable(exports, surround, insert, 'prerender', `boolean | 'auto'`);
5527
- addTypeToVariable(exports, surround, insert, 'trailingSlash', `'never' | 'always' | 'ignore'`);
5528
- addTypeToVariable(exports, surround, insert, 'ssr', `boolean`);
5529
- addTypeToVariable(exports, surround, insert, 'csr', `boolean`);
5720
+ if (isTsFile) {
5721
+ const pos = actions.node.initializer.getEnd();
5722
+ const inserted = surround(` satisfies import('./$types.js').Actions`);
5723
+ insert(pos, inserted);
5724
+ }
5725
+ else {
5726
+ addJsDocSatisfiesToVariable(surround, insert, actions.node, `import('./$types.js').Actions`);
5727
+ }
5728
+ }
5729
+ addTypeToVariable(exports, surround, insert, isTsFile, 'prerender', `boolean | 'auto'`);
5730
+ addTypeToVariable(exports, surround, insert, isTsFile, 'trailingSlash', `'never' | 'always' | 'ignore'`);
5731
+ addTypeToVariable(exports, surround, insert, isTsFile, 'ssr', `boolean`);
5732
+ addTypeToVariable(exports, surround, insert, isTsFile, 'csr', `boolean`);
5530
5733
  // add types to GET/PUT/POST/PATCH/DELETE/OPTIONS/HEAD if not explicitly typed
5531
5734
  const insertApiMethod = (name) => {
5532
- addTypeToFunction(ts, exports, surround, insert, name, `import('./$types.js').RequestEvent`, `Response | Promise<Response>`);
5735
+ addTypeToFunction(ts, exports, surround, insert, isTsFile, name, `import('./$types.js').RequestEvent`, `Response | Promise<Response>`);
5533
5736
  };
5534
5737
  insertApiMethod('GET');
5535
5738
  insertApiMethod('PUT');
@@ -5554,7 +5757,7 @@ function upsertKitParamsFile(ts, fileName, basename, paramsPath, getSource, surr
5554
5757
  };
5555
5758
  const isTsFile = basename.endsWith('.ts');
5556
5759
  const exports = findExports(ts, source, isTsFile);
5557
- addTypeToFunction(ts, exports, surround, insert, 'match', 'string', 'boolean');
5760
+ addTypeToFunction(ts, exports, surround, insert, isTsFile, 'match', 'string', 'boolean');
5558
5761
  return { addedCode, originalText: source.getFullText() };
5559
5762
  }
5560
5763
  function upsertKitClientHooksFile(ts, fileName, basename, clientHooksPath, getSource, surround) {
@@ -5570,7 +5773,7 @@ function upsertKitClientHooksFile(ts, fileName, basename, clientHooksPath, getSo
5570
5773
  };
5571
5774
  const isTsFile = basename.endsWith('.ts');
5572
5775
  const exports = findExports(ts, source, isTsFile);
5573
- addTypeToFunction(ts, exports, surround, insert, 'handleError', `import('@sveltejs/kit').HandleClientError`);
5776
+ addTypeToFunction(ts, exports, surround, insert, isTsFile, 'handleError', `import('@sveltejs/kit').HandleClientError`);
5574
5777
  return { addedCode, originalText: source.getFullText() };
5575
5778
  }
5576
5779
  function upsertKitServerHooksFile(ts, fileName, basename, serverHooksPath, getSource, surround) {
@@ -5587,7 +5790,7 @@ function upsertKitServerHooksFile(ts, fileName, basename, serverHooksPath, getSo
5587
5790
  const isTsFile = basename.endsWith('.ts');
5588
5791
  const exports = findExports(ts, source, isTsFile);
5589
5792
  const addType = (name, type) => {
5590
- addTypeToFunction(ts, exports, surround, insert, name, type);
5793
+ addTypeToFunction(ts, exports, surround, insert, isTsFile, name, type);
5591
5794
  };
5592
5795
  addType('handleError', `import('@sveltejs/kit').HandleServerError`);
5593
5796
  addType('handle', `import('@sveltejs/kit').Handle`);
@@ -5607,32 +5810,68 @@ function upsertKitUniversalHooksFile(ts, fileName, basename, universalHooksPath,
5607
5810
  };
5608
5811
  const isTsFile = basename.endsWith('.ts');
5609
5812
  const exports = findExports(ts, source, isTsFile);
5610
- addTypeToFunction(ts, exports, surround, insert, 'reroute', `import('@sveltejs/kit').Reroute`);
5813
+ addTypeToFunction(ts, exports, surround, insert, isTsFile, 'reroute', `import('@sveltejs/kit').Reroute`);
5611
5814
  return { addedCode, originalText: source.getFullText() };
5612
5815
  }
5613
- function addTypeToVariable(exports, surround, insert, name, type) {
5816
+ function addTypeToVariable(exports, surround, insert, isTsFile, name, type) {
5614
5817
  const variable = exports.get(name);
5615
5818
  if ((variable === null || variable === void 0 ? void 0 : variable.type) === 'var' && !variable.hasTypeDefinition && variable.node.initializer) {
5616
- const pos = variable.node.name.getEnd();
5617
- const inserted = surround(` : ${type}`);
5618
- insert(pos, inserted);
5819
+ if (isTsFile) {
5820
+ const pos = variable.node.name.getEnd();
5821
+ const inserted = surround(` : ${type}`);
5822
+ insert(pos, inserted);
5823
+ }
5824
+ else {
5825
+ addJsDocTypeToVariable(surround, insert, variable.node, type);
5826
+ }
5619
5827
  }
5620
5828
  }
5621
- function addTypeToFunction(ts, exports, surround, insert, name, type, returnType) {
5829
+ function addTypeToFunction(ts, exports, surround, insert, isTsFile, name, type, returnType) {
5622
5830
  const fn = exports.get(name);
5623
5831
  if ((fn === null || fn === void 0 ? void 0 : fn.type) === 'function' && fn.node.parameters.length === 1 && !fn.hasTypeDefinition) {
5624
- const paramPos = fn.node.parameters[0].getEnd();
5625
- const paramInsertion = surround(!returnType ? `: Parameters<${type}>[0]` : `: ${type}`);
5626
- insert(paramPos, paramInsertion);
5627
- if (!fn.node.type && fn.node.body) {
5628
- const returnPos = ts.isArrowFunction(fn.node)
5629
- ? fn.node.equalsGreaterThanToken.getStart()
5630
- : fn.node.body.getStart();
5631
- const returnInsertion = surround(!returnType ? `: ReturnType<${type}> ` : `: ${returnType} `);
5632
- insert(returnPos, returnInsertion);
5832
+ if (isTsFile) {
5833
+ const paramPos = fn.node.parameters[0].getEnd();
5834
+ const paramInsertion = surround(!returnType ? `: Parameters<${type}>[0]` : `: ${type}`);
5835
+ insert(paramPos, paramInsertion);
5836
+ if (!fn.node.type && fn.node.body) {
5837
+ const returnPos = ts.isArrowFunction(fn.node)
5838
+ ? fn.node.equalsGreaterThanToken.getStart()
5839
+ : fn.node.body.getStart();
5840
+ const returnInsertion = surround(!returnType ? `: ReturnType<${type}> ` : `: ${returnType} `);
5841
+ insert(returnPos, returnInsertion);
5842
+ }
5843
+ }
5844
+ else {
5845
+ const jsdoc_type = returnType === undefined ? type : `(arg0: ${type}) => ${returnType}`;
5846
+ addJsDocTypeToFunction(surround, insert, fn.node, jsdoc_type);
5633
5847
  }
5634
5848
  }
5635
5849
  }
5850
+ function addJsDocTypeToVariable(surround, insert, node, type) {
5851
+ if (!node.initializer) {
5852
+ return;
5853
+ }
5854
+ insert(node.initializer.getStart(), surround(`/** @type {${type}} */ (`));
5855
+ insert(node.initializer.getEnd(), surround(`)`));
5856
+ }
5857
+ function addJsDocSatisfiesToVariable(surround, insert, node, type) {
5858
+ if (!node.initializer) {
5859
+ return;
5860
+ }
5861
+ insert(node.initializer.getStart(), surround(`/** @satisfies {${type}} */ (`));
5862
+ insert(node.initializer.getEnd(), surround(`)`));
5863
+ }
5864
+ function addJsDocTypeToFunction(surround, insert, node, type) {
5865
+ insert(node.getStart(), surround(`/** @type {${type}} */ `));
5866
+ }
5867
+ function addJsDocParamToFunction(surround, insert, ts, node, type) {
5868
+ const parameter = node.parameters[0];
5869
+ if (!parameter) {
5870
+ return;
5871
+ }
5872
+ const parameter_name = ts.isIdentifier(parameter.name) ? parameter.name.text : 'arg0';
5873
+ insert(node.getStart(), surround(`/** @param {${type}} ${parameter_name} */ `));
5874
+ }
5636
5875
  function insertCode(addedCode, pos, inserted) {
5637
5876
  var _a, _b, _c, _d;
5638
5877
  const insertionIdx = addedCode.findIndex((c) => c.originalPos > pos);
@@ -7686,7 +7925,7 @@ class InterfacesAndTypes {
7686
7925
  }
7687
7926
  }
7688
7927
 
7689
- function processInstanceScriptContent(str, script, events, implicitStoreValues, mode, moduleAst, isTSFile, basename, isSvelte5Plus, isRunes, emitJsDoc) {
7928
+ function processInstanceScriptContent(str, script, events, implicitStoreValues, mode, moduleAst, isTSFile, basename, isSvelte5Plus, isRunes, emitJsDoc, rewriteExternalImports) {
7690
7929
  const htmlx = str.original;
7691
7930
  const scriptContent = htmlx.substring(script.content.start, script.content.end);
7692
7931
  const tsAst = ts.createSourceFile('component.ts.svelte', scriptContent, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
@@ -7798,6 +8037,11 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
7798
8037
  const walk = (node, parent) => {
7799
8038
  var _a, _b, _c;
7800
8039
  const onLeaveCallbacks = [];
8040
+ if (rewriteExternalImports) {
8041
+ rewriteExternalImportsInNode(ts, node, rewriteExternalImports, (specifier, rewrite) => {
8042
+ str.overwrite(specifier.getStart(tsAst) + astOffset + 1, specifier.getEnd() + astOffset - 1, rewrite.rewritten);
8043
+ });
8044
+ }
7801
8045
  if (parent === tsAst) {
7802
8046
  exportedNames.hoistableInterfaces.analyzeInstanceScriptNode(node);
7803
8047
  }
@@ -7981,7 +8225,7 @@ function createModuleAst(str, script) {
7981
8225
  const astOffset = script.content.start;
7982
8226
  return { htmlx, tsAst, astOffset };
7983
8227
  }
7984
- function processModuleScriptTag(str, script, implicitStoreValues, moduleAst) {
8228
+ function processModuleScriptTag(str, script, implicitStoreValues, moduleAst, rewriteExternalImports) {
7985
8229
  const { htmlx, tsAst, astOffset } = moduleAst;
7986
8230
  const generics = new Generics(str, astOffset, script);
7987
8231
  if (generics.genericsAttr) {
@@ -7989,6 +8233,11 @@ function processModuleScriptTag(str, script, implicitStoreValues, moduleAst) {
7989
8233
  throwError(start, start + 8, 'The generics attribute is only allowed on the instance script', str.original);
7990
8234
  }
7991
8235
  const walk = (node) => {
8236
+ if (rewriteExternalImports) {
8237
+ rewriteExternalImportsInNode(ts, node, rewriteExternalImports, (specifier, rewrite) => {
8238
+ str.overwrite(specifier.getStart(tsAst) + astOffset + 1, specifier.getEnd() + astOffset - 1, rewrite.rewritten);
8239
+ });
8240
+ }
7992
8241
  resolveImplicitStoreValue(node, implicitStoreValues, str, astOffset);
7993
8242
  generics.throwIfIsGeneric(node);
7994
8243
  throwIfIs$$EventsDeclaration(node, str, astOffset);
@@ -8052,6 +8301,13 @@ function svelte2tsx(svelte, options = { parse: compiler.parse }) {
8052
8301
  options.mode = options.mode || 'ts';
8053
8302
  options.version = options.version || compiler.VERSION;
8054
8303
  const str = new MagicString(svelte);
8304
+ const rewriteExternalImportsOptions = options.rewriteExternalImports && options.filename
8305
+ ? {
8306
+ sourcePath: options.filename,
8307
+ generatedPath: options.rewriteExternalImports.generatedPath,
8308
+ workspacePath: options.rewriteExternalImports.workspacePath
8309
+ }
8310
+ : undefined;
8055
8311
  const basename = path.basename(options.filename || '');
8056
8312
  const svelte5Plus = Number(options.version[0]) > 4;
8057
8313
  const isTsFile = options === null || options === void 0 ? void 0 : options.isTsFile;
@@ -8059,7 +8315,8 @@ function svelte2tsx(svelte, options = { parse: compiler.parse }) {
8059
8315
  // process the htmlx as a svelte template
8060
8316
  let { htmlAst, moduleScriptTag, scriptTag, rootSnippets, slots, uses$$props, uses$$slots, uses$$restProps, events, componentDocumentation, resolvedStores, usesAccessors, isRunes } = processSvelteTemplate(str, options.parse || compiler.parse, {
8061
8317
  ...options,
8062
- svelte5Plus
8318
+ svelte5Plus,
8319
+ rewriteExternalImports: rewriteExternalImportsOptions
8063
8320
  });
8064
8321
  /* Rearrange the script tags so that module is first, and instance second followed finally by the template
8065
8322
  * This is a bit convoluted due to some trouble I had with magic string. A simple str.move(start,end,0) for each script wasn't enough
@@ -8093,7 +8350,7 @@ function svelte2tsx(svelte, options = { parse: compiler.parse }) {
8093
8350
  if (scriptTag.start != instanceScriptTarget) {
8094
8351
  str.move(scriptTag.start, scriptTag.end, instanceScriptTarget);
8095
8352
  }
8096
- const res = processInstanceScriptContent(str, scriptTag, events, implicitStoreValues, options.mode, moduleAst, isTsFile, basename, svelte5Plus, isRunes, emitJsDoc);
8353
+ const res = processInstanceScriptContent(str, scriptTag, events, implicitStoreValues, options.mode, moduleAst, isTsFile, basename, svelte5Plus, isRunes, emitJsDoc, rewriteExternalImportsOptions);
8097
8354
  uses$$props = uses$$props || res.uses$$props;
8098
8355
  uses$$restProps = uses$$restProps || res.uses$$restProps;
8099
8356
  uses$$slots = uses$$slots || res.uses$$slots;
@@ -8127,7 +8384,7 @@ function svelte2tsx(svelte, options = { parse: compiler.parse }) {
8127
8384
  });
8128
8385
  // we need to process the module script after the instance script has moved otherwise we get warnings about moving edited items
8129
8386
  if (moduleScriptTag) {
8130
- processModuleScriptTag(str, moduleScriptTag, new ImplicitStoreValues(implicitStoreValues.getAccessedStores(), renderFunctionStart, svelte5Plus, scriptTag || options.mode === 'ts' ? undefined : (input) => `</>;${input}<>`), moduleAst);
8387
+ processModuleScriptTag(str, moduleScriptTag, new ImplicitStoreValues(implicitStoreValues.getAccessedStores(), renderFunctionStart, svelte5Plus, scriptTag || options.mode === 'ts' ? undefined : (input) => `</>;${input}<>`), moduleAst, rewriteExternalImportsOptions);
8131
8388
  if (!scriptTag) {
8132
8389
  moduleAst.tsAst.forEachChild((node) => exportedNames.hoistableInterfaces.analyzeModuleScriptNode(node));
8133
8390
  }
package/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import dedent from 'dedent-js';
2
2
  import ts from 'typescript';
3
- import { pascalCase } from 'scule';
4
3
  import * as path from 'path';
5
4
  import path__default, { join, resolve, dirname, basename } from 'path';
5
+ import { pascalCase } from 'scule';
6
6
  import { VERSION, parse } from 'svelte/compiler';
7
7
 
8
8
  const comma = ','.charCodeAt(0);
@@ -4877,6 +4877,109 @@ function handleAttachTag(tag, element) {
4877
4877
  }
4878
4878
  }
4879
4879
 
4880
+ function toPosixPath(value) {
4881
+ return value.replace(/\\/g, '/');
4882
+ }
4883
+ function isWithinDirectory(filePath, directoryPath) {
4884
+ const relative = path__default.relative(path__default.resolve(directoryPath), path__default.resolve(filePath));
4885
+ return relative === '' || (!relative.startsWith('..') && !path__default.isAbsolute(relative));
4886
+ }
4887
+ function splitImportSpecifier(specifier) {
4888
+ const queryIndex = specifier.indexOf('?');
4889
+ const hashIndex = specifier.indexOf('#');
4890
+ const cutIndex = queryIndex === -1
4891
+ ? hashIndex
4892
+ : hashIndex === -1
4893
+ ? queryIndex
4894
+ : Math.min(queryIndex, hashIndex);
4895
+ if (cutIndex === -1) {
4896
+ return { pathPart: specifier, suffix: '' };
4897
+ }
4898
+ return {
4899
+ pathPart: specifier.slice(0, cutIndex),
4900
+ suffix: specifier.slice(cutIndex)
4901
+ };
4902
+ }
4903
+ function getExternalImportRewrite(specifier, options) {
4904
+ const sourceDir = path__default.dirname(options.sourcePath);
4905
+ const generatedDir = path__default.dirname(options.generatedPath);
4906
+ const { pathPart, suffix } = splitImportSpecifier(specifier);
4907
+ if (!pathPart.startsWith('../')) {
4908
+ return null;
4909
+ }
4910
+ const targetPath = path__default.resolve(sourceDir, pathPart);
4911
+ if (isWithinDirectory(targetPath, options.workspacePath)) {
4912
+ return null;
4913
+ }
4914
+ const rewrittenRelative = toPosixPath(path__default.relative(generatedDir, targetPath));
4915
+ const rewritten = `${rewrittenRelative}${suffix}`;
4916
+ if (rewritten === specifier) {
4917
+ return null;
4918
+ }
4919
+ return {
4920
+ rewritten,
4921
+ insertedPrefix: rewrittenRelative.slice(0, rewrittenRelative.length - pathPart.length)
4922
+ };
4923
+ }
4924
+ function getImportTypeSpecifierLiteral(ts_impl, node) {
4925
+ const argument = node.argument;
4926
+ if (ts_impl.isLiteralTypeNode(argument) && ts_impl.isStringLiteralLike(argument.literal)) {
4927
+ return argument.literal;
4928
+ }
4929
+ return undefined;
4930
+ }
4931
+ function rewriteImportTypesInNode(ts_impl, node, applyImportRewrite) {
4932
+ if (ts_impl.isImportTypeNode(node)) {
4933
+ const specifier = getImportTypeSpecifierLiteral(ts_impl, node);
4934
+ if (specifier) {
4935
+ applyImportRewrite(specifier);
4936
+ }
4937
+ }
4938
+ ts_impl.forEachChild(node, (child) => rewriteImportTypesInNode(ts_impl, child, applyImportRewrite));
4939
+ }
4940
+ function rewriteExternalImportsInNode(ts_impl, node, options, on_rewrite) {
4941
+ const applyImportRewrite = (module_specifier) => {
4942
+ const rewrite = getExternalImportRewrite(module_specifier.text, options);
4943
+ if (rewrite) {
4944
+ on_rewrite(module_specifier, rewrite);
4945
+ }
4946
+ };
4947
+ if (ts_impl.isImportDeclaration(node) || ts_impl.isExportDeclaration(node)) {
4948
+ if (node.moduleSpecifier && ts_impl.isStringLiteralLike(node.moduleSpecifier)) {
4949
+ applyImportRewrite(node.moduleSpecifier);
4950
+ }
4951
+ }
4952
+ else if (ts_impl.isCallExpression(node)) {
4953
+ const firstArg = node.arguments[0];
4954
+ if (firstArg && ts_impl.isStringLiteralLike(firstArg)) {
4955
+ const isDynamicImport = node.expression.kind === ts_impl.SyntaxKind.ImportKeyword;
4956
+ const isRequireCall = ts_impl.isIdentifier(node.expression) && node.expression.text === 'require';
4957
+ if (isDynamicImport || isRequireCall) {
4958
+ applyImportRewrite(firstArg);
4959
+ }
4960
+ }
4961
+ }
4962
+ else if (ts_impl.isImportTypeNode(node)) {
4963
+ const specifier = getImportTypeSpecifierLiteral(ts_impl, node);
4964
+ if (specifier) {
4965
+ applyImportRewrite(specifier);
4966
+ }
4967
+ }
4968
+ const jsDoc = node.jsDoc;
4969
+ if (jsDoc) {
4970
+ for (const doc of jsDoc) {
4971
+ rewriteImportTypesInNode(ts_impl, doc, applyImportRewrite);
4972
+ }
4973
+ }
4974
+ }
4975
+ function forEachExternalImportRewrite(ts_impl, source, options, on_rewrite) {
4976
+ const visit = (node) => {
4977
+ rewriteExternalImportsInNode(ts_impl, node, options, on_rewrite);
4978
+ ts_impl.forEachChild(node, visit);
4979
+ };
4980
+ ts_impl.forEachChild(source, visit);
4981
+ }
4982
+
4880
4983
  /**
4881
4984
  * Walks the HTMLx part of the Svelte component
4882
4985
  * and converts it to JSX
@@ -4948,6 +5051,45 @@ function convertHtmlxToJsx(str, ast, tags, options = { svelte5Plus: false }) {
4948
5051
  const handleStyleTag = (node) => {
4949
5052
  str.remove(node.start, node.end);
4950
5053
  };
5054
+ const rewriteImportSpecifier = (importSpecifier) => {
5055
+ var _a;
5056
+ if (!options.rewriteExternalImports) {
5057
+ return importSpecifier;
5058
+ }
5059
+ const rewrite = getExternalImportRewrite(importSpecifier, options.rewriteExternalImports);
5060
+ return (_a = rewrite === null || rewrite === void 0 ? void 0 : rewrite.rewritten) !== null && _a !== void 0 ? _a : importSpecifier;
5061
+ };
5062
+ const rewriteImportsInComment = (comment) => {
5063
+ if (!options.rewriteExternalImports) {
5064
+ return;
5065
+ }
5066
+ const original = str.original.slice(comment.start, comment.end);
5067
+ const rewritten = original.replace(/import\(\s*(['"])([^'"]+)\1\s*\)/g, (fullMatch, quote, importSpecifier) => {
5068
+ const specifier = rewriteImportSpecifier(importSpecifier);
5069
+ return specifier === importSpecifier
5070
+ ? fullMatch
5071
+ : `import(${quote}${specifier}${quote})`;
5072
+ });
5073
+ if (rewritten !== original) {
5074
+ str.overwrite(comment.start, comment.end, rewritten);
5075
+ }
5076
+ };
5077
+ const rewriteNodeCommentImports = (node) => {
5078
+ var _a, _b, _c;
5079
+ if (!options.rewriteExternalImports) {
5080
+ return;
5081
+ }
5082
+ const nodeWithcomments = node;
5083
+ for (const comment of (_a = nodeWithcomments.leadingComments) !== null && _a !== void 0 ? _a : []) {
5084
+ rewriteImportsInComment(comment);
5085
+ }
5086
+ for (const comment of (_b = nodeWithcomments.trailingComments) !== null && _b !== void 0 ? _b : []) {
5087
+ rewriteImportsInComment(comment);
5088
+ }
5089
+ for (const comment of (_c = nodeWithcomments.innerComments) !== null && _c !== void 0 ? _c : []) {
5090
+ rewriteImportsInComment(comment);
5091
+ }
5092
+ };
4951
5093
  const slotHandler = new SlotHandler(str.original);
4952
5094
  let templateScope = new TemplateScope();
4953
5095
  const handleComponentLet = (component) => {
@@ -4988,12 +5130,25 @@ function convertHtmlxToJsx(str, ast, tags, options = { svelte5Plus: false }) {
4988
5130
  isDeclaration.value = true;
4989
5131
  }
4990
5132
  try {
5133
+ rewriteNodeCommentImports(node);
4991
5134
  switch (node.type) {
4992
5135
  case 'Identifier':
4993
5136
  handleIdentifier(node);
4994
5137
  stores.handleIdentifier(node, parent, prop);
4995
5138
  eventHandler.handleIdentifier(node, parent, prop);
4996
5139
  break;
5140
+ case 'ImportExpression': {
5141
+ const source = node.source;
5142
+ if (options.rewriteExternalImports &&
5143
+ (source === null || source === void 0 ? void 0 : source.type) === 'Literal' &&
5144
+ typeof source.value === 'string') {
5145
+ const rewrite = getExternalImportRewrite(source.value, options.rewriteExternalImports);
5146
+ if (rewrite) {
5147
+ str.overwrite(source.start + 1, source.end - 1, rewrite.rewritten);
5148
+ }
5149
+ }
5150
+ break;
5151
+ }
4997
5152
  case 'IfBlock':
4998
5153
  handleIf(str, node);
4999
5154
  break;
@@ -5355,6 +5510,8 @@ function findExports(ts, source, isTsFile) {
5355
5510
  const declaration = statement.declarationList.declarations[0];
5356
5511
  const hasTypeDefinition = !!declaration.type ||
5357
5512
  (!isTsFile && !!ts.getJSDocType(declaration)) ||
5513
+ (!isTsFile &&
5514
+ ts.getJSDocTags(declaration).some((t) => t.tagName.text === 'satisfies')) ||
5358
5515
  (!!declaration.initializer && ts.isSatisfiesExpression(declaration.initializer));
5359
5516
  if (declaration.initializer &&
5360
5517
  (ts.isFunctionExpression(declaration.initializer) ||
@@ -5439,7 +5596,7 @@ function isParamsFile(fileName, basename, paramsPath) {
5439
5596
  !basename.includes('.test') &&
5440
5597
  !basename.includes('.spec'));
5441
5598
  }
5442
- function upsertKitFile(ts, fileName, kitFilesSettings, getSource, surround = (text) => text) {
5599
+ function upsertKitFile(ts, fileName, kitFilesSettings, getSource, surround = (text) => text, rewriteExternalImports) {
5443
5600
  var _a, _b, _c, _d;
5444
5601
  let basename = path__default.basename(fileName);
5445
5602
  const result = (_d = (_c = (_b = (_a = upsertKitRouteFile(ts, basename, getSource, surround)) !== null && _a !== void 0 ? _a : upsertKitServerHooksFile(ts, fileName, basename, kitFilesSettings.serverHooksPath, getSource, surround)) !== null && _b !== void 0 ? _b : upsertKitClientHooksFile(ts, fileName, basename, kitFilesSettings.clientHooksPath, getSource, surround)) !== null && _c !== void 0 ? _c : upsertKitUniversalHooksFile(ts, fileName, basename, kitFilesSettings.universalHooksPath, getSource, surround)) !== null && _d !== void 0 ? _d : upsertKitParamsFile(ts, fileName, basename, kitFilesSettings.paramsPath, getSource, surround);
@@ -5455,8 +5612,34 @@ function upsertKitFile(ts, fileName, kitFilesSettings, getSource, surround = (te
5455
5612
  pos = added.originalPos;
5456
5613
  }
5457
5614
  text += originalText.slice(pos);
5615
+ if (rewriteExternalImports) {
5616
+ const source = getSource();
5617
+ if (source) {
5618
+ const rewriteOptions = {
5619
+ sourcePath: fileName,
5620
+ generatedPath: rewriteExternalImports.generatedPath,
5621
+ workspacePath: rewriteExternalImports.workspacePath
5622
+ };
5623
+ applyExternalImportRewritesToAddedCode(ts, source, addedCode, rewriteOptions);
5624
+ let rewritePos = 0;
5625
+ text = '';
5626
+ for (const added of addedCode) {
5627
+ text += originalText.slice(rewritePos, added.originalPos) + added.inserted;
5628
+ rewritePos = added.originalPos;
5629
+ }
5630
+ text += originalText.slice(rewritePos);
5631
+ }
5632
+ }
5458
5633
  return { text, addedCode };
5459
5634
  }
5635
+ function applyExternalImportRewritesToAddedCode(ts_impl, source, addedCode, rewriteOptions) {
5636
+ forEachExternalImportRewrite(ts_impl, source, rewriteOptions, (module_specifier, rewrite) => {
5637
+ if (!rewrite.insertedPrefix) {
5638
+ return;
5639
+ }
5640
+ insertCode(addedCode, module_specifier.getStart(source) + 1, rewrite.insertedPrefix);
5641
+ });
5642
+ }
5460
5643
  function upsertKitRouteFile(ts, basename, getSource, surround) {
5461
5644
  if (!isKitRouteFile(basename))
5462
5645
  return;
@@ -5472,15 +5655,27 @@ function upsertKitRouteFile(ts, basename, getSource, surround) {
5472
5655
  // add type to load function if not explicitly typed
5473
5656
  const load = exports.get('load');
5474
5657
  if ((load === null || load === void 0 ? void 0 : load.type) === 'function' && load.node.parameters.length === 1 && !load.hasTypeDefinition) {
5475
- const pos = load.node.parameters[0].getEnd();
5476
- const inserted = surround(`: import('./$types.js').${basename.includes('layout') ? 'Layout' : 'Page'}${basename.includes('server') ? 'Server' : ''}LoadEvent`);
5477
- insert(pos, inserted);
5658
+ const load_type = `import('./$types.js').${basename.includes('layout') ? 'Layout' : 'Page'}${basename.includes('server') ? 'Server' : ''}Load`;
5659
+ if (isTsFile) {
5660
+ const pos = load.node.parameters[0].getEnd();
5661
+ const inserted = surround(`: ${load_type}Event`);
5662
+ insert(pos, inserted);
5663
+ }
5664
+ else {
5665
+ addJsDocParamToFunction(surround, insert, ts, load.node, `${load_type}Event`);
5666
+ }
5478
5667
  }
5479
5668
  else if ((load === null || load === void 0 ? void 0 : load.type) === 'var' && !load.hasTypeDefinition) {
5480
- // "const load = ..." will be transformed into
5481
- // "const load = (...) satisfies PageLoad"
5482
- insert(load.node.initializer.getStart(), surround('('));
5483
- insert(load.node.initializer.getEnd(), surround(`) satisfies import('./$types.js').${basename.includes('layout') ? 'Layout' : 'Page'}${basename.includes('server') ? 'Server' : ''}Load`));
5669
+ const load_type = `import('./$types.js').${basename.includes('layout') ? 'Layout' : 'Page'}${basename.includes('server') ? 'Server' : ''}Load`;
5670
+ if (isTsFile) {
5671
+ // "const load = ..." will be transformed into
5672
+ // "const load = (...) satisfies PageLoad"
5673
+ insert(load.node.initializer.getStart(), surround('('));
5674
+ insert(load.node.initializer.getEnd(), surround(`) satisfies ${load_type}`));
5675
+ }
5676
+ else {
5677
+ addJsDocSatisfiesToVariable(surround, insert, load.node, load_type);
5678
+ }
5484
5679
  }
5485
5680
  // add type to entries function if not explicitly typed
5486
5681
  const entries = exports.get('entries');
@@ -5488,28 +5683,36 @@ function upsertKitRouteFile(ts, basename, getSource, surround) {
5488
5683
  entries.node.parameters.length === 0 &&
5489
5684
  !entries.hasTypeDefinition &&
5490
5685
  !basename.includes('layout')) {
5491
- if (!entries.node.type && entries.node.body) {
5686
+ if (isTsFile && !entries.node.type && entries.node.body) {
5492
5687
  const returnPos = ts.isArrowFunction(entries.node)
5493
5688
  ? entries.node.equalsGreaterThanToken.getStart()
5494
5689
  : entries.node.body.getStart();
5495
5690
  const returnInsertion = surround(`: ReturnType<import('./$types.js').EntryGenerator> `);
5496
5691
  insert(returnPos, returnInsertion);
5497
5692
  }
5693
+ else if (!isTsFile) {
5694
+ addJsDocTypeToFunction(surround, insert, entries.node, `import('./$types.js').EntryGenerator`);
5695
+ }
5498
5696
  }
5499
5697
  // add type to actions variable if not explicitly typed
5500
5698
  const actions = exports.get('actions');
5501
5699
  if ((actions === null || actions === void 0 ? void 0 : actions.type) === 'var' && !actions.hasTypeDefinition && actions.node.initializer) {
5502
- const pos = actions.node.initializer.getEnd();
5503
- const inserted = surround(` satisfies import('./$types.js').Actions`);
5504
- insert(pos, inserted);
5505
- }
5506
- addTypeToVariable(exports, surround, insert, 'prerender', `boolean | 'auto'`);
5507
- addTypeToVariable(exports, surround, insert, 'trailingSlash', `'never' | 'always' | 'ignore'`);
5508
- addTypeToVariable(exports, surround, insert, 'ssr', `boolean`);
5509
- addTypeToVariable(exports, surround, insert, 'csr', `boolean`);
5700
+ if (isTsFile) {
5701
+ const pos = actions.node.initializer.getEnd();
5702
+ const inserted = surround(` satisfies import('./$types.js').Actions`);
5703
+ insert(pos, inserted);
5704
+ }
5705
+ else {
5706
+ addJsDocSatisfiesToVariable(surround, insert, actions.node, `import('./$types.js').Actions`);
5707
+ }
5708
+ }
5709
+ addTypeToVariable(exports, surround, insert, isTsFile, 'prerender', `boolean | 'auto'`);
5710
+ addTypeToVariable(exports, surround, insert, isTsFile, 'trailingSlash', `'never' | 'always' | 'ignore'`);
5711
+ addTypeToVariable(exports, surround, insert, isTsFile, 'ssr', `boolean`);
5712
+ addTypeToVariable(exports, surround, insert, isTsFile, 'csr', `boolean`);
5510
5713
  // add types to GET/PUT/POST/PATCH/DELETE/OPTIONS/HEAD if not explicitly typed
5511
5714
  const insertApiMethod = (name) => {
5512
- addTypeToFunction(ts, exports, surround, insert, name, `import('./$types.js').RequestEvent`, `Response | Promise<Response>`);
5715
+ addTypeToFunction(ts, exports, surround, insert, isTsFile, name, `import('./$types.js').RequestEvent`, `Response | Promise<Response>`);
5513
5716
  };
5514
5717
  insertApiMethod('GET');
5515
5718
  insertApiMethod('PUT');
@@ -5534,7 +5737,7 @@ function upsertKitParamsFile(ts, fileName, basename, paramsPath, getSource, surr
5534
5737
  };
5535
5738
  const isTsFile = basename.endsWith('.ts');
5536
5739
  const exports = findExports(ts, source, isTsFile);
5537
- addTypeToFunction(ts, exports, surround, insert, 'match', 'string', 'boolean');
5740
+ addTypeToFunction(ts, exports, surround, insert, isTsFile, 'match', 'string', 'boolean');
5538
5741
  return { addedCode, originalText: source.getFullText() };
5539
5742
  }
5540
5743
  function upsertKitClientHooksFile(ts, fileName, basename, clientHooksPath, getSource, surround) {
@@ -5550,7 +5753,7 @@ function upsertKitClientHooksFile(ts, fileName, basename, clientHooksPath, getSo
5550
5753
  };
5551
5754
  const isTsFile = basename.endsWith('.ts');
5552
5755
  const exports = findExports(ts, source, isTsFile);
5553
- addTypeToFunction(ts, exports, surround, insert, 'handleError', `import('@sveltejs/kit').HandleClientError`);
5756
+ addTypeToFunction(ts, exports, surround, insert, isTsFile, 'handleError', `import('@sveltejs/kit').HandleClientError`);
5554
5757
  return { addedCode, originalText: source.getFullText() };
5555
5758
  }
5556
5759
  function upsertKitServerHooksFile(ts, fileName, basename, serverHooksPath, getSource, surround) {
@@ -5567,7 +5770,7 @@ function upsertKitServerHooksFile(ts, fileName, basename, serverHooksPath, getSo
5567
5770
  const isTsFile = basename.endsWith('.ts');
5568
5771
  const exports = findExports(ts, source, isTsFile);
5569
5772
  const addType = (name, type) => {
5570
- addTypeToFunction(ts, exports, surround, insert, name, type);
5773
+ addTypeToFunction(ts, exports, surround, insert, isTsFile, name, type);
5571
5774
  };
5572
5775
  addType('handleError', `import('@sveltejs/kit').HandleServerError`);
5573
5776
  addType('handle', `import('@sveltejs/kit').Handle`);
@@ -5587,32 +5790,68 @@ function upsertKitUniversalHooksFile(ts, fileName, basename, universalHooksPath,
5587
5790
  };
5588
5791
  const isTsFile = basename.endsWith('.ts');
5589
5792
  const exports = findExports(ts, source, isTsFile);
5590
- addTypeToFunction(ts, exports, surround, insert, 'reroute', `import('@sveltejs/kit').Reroute`);
5793
+ addTypeToFunction(ts, exports, surround, insert, isTsFile, 'reroute', `import('@sveltejs/kit').Reroute`);
5591
5794
  return { addedCode, originalText: source.getFullText() };
5592
5795
  }
5593
- function addTypeToVariable(exports, surround, insert, name, type) {
5796
+ function addTypeToVariable(exports, surround, insert, isTsFile, name, type) {
5594
5797
  const variable = exports.get(name);
5595
5798
  if ((variable === null || variable === void 0 ? void 0 : variable.type) === 'var' && !variable.hasTypeDefinition && variable.node.initializer) {
5596
- const pos = variable.node.name.getEnd();
5597
- const inserted = surround(` : ${type}`);
5598
- insert(pos, inserted);
5799
+ if (isTsFile) {
5800
+ const pos = variable.node.name.getEnd();
5801
+ const inserted = surround(` : ${type}`);
5802
+ insert(pos, inserted);
5803
+ }
5804
+ else {
5805
+ addJsDocTypeToVariable(surround, insert, variable.node, type);
5806
+ }
5599
5807
  }
5600
5808
  }
5601
- function addTypeToFunction(ts, exports, surround, insert, name, type, returnType) {
5809
+ function addTypeToFunction(ts, exports, surround, insert, isTsFile, name, type, returnType) {
5602
5810
  const fn = exports.get(name);
5603
5811
  if ((fn === null || fn === void 0 ? void 0 : fn.type) === 'function' && fn.node.parameters.length === 1 && !fn.hasTypeDefinition) {
5604
- const paramPos = fn.node.parameters[0].getEnd();
5605
- const paramInsertion = surround(!returnType ? `: Parameters<${type}>[0]` : `: ${type}`);
5606
- insert(paramPos, paramInsertion);
5607
- if (!fn.node.type && fn.node.body) {
5608
- const returnPos = ts.isArrowFunction(fn.node)
5609
- ? fn.node.equalsGreaterThanToken.getStart()
5610
- : fn.node.body.getStart();
5611
- const returnInsertion = surround(!returnType ? `: ReturnType<${type}> ` : `: ${returnType} `);
5612
- insert(returnPos, returnInsertion);
5812
+ if (isTsFile) {
5813
+ const paramPos = fn.node.parameters[0].getEnd();
5814
+ const paramInsertion = surround(!returnType ? `: Parameters<${type}>[0]` : `: ${type}`);
5815
+ insert(paramPos, paramInsertion);
5816
+ if (!fn.node.type && fn.node.body) {
5817
+ const returnPos = ts.isArrowFunction(fn.node)
5818
+ ? fn.node.equalsGreaterThanToken.getStart()
5819
+ : fn.node.body.getStart();
5820
+ const returnInsertion = surround(!returnType ? `: ReturnType<${type}> ` : `: ${returnType} `);
5821
+ insert(returnPos, returnInsertion);
5822
+ }
5823
+ }
5824
+ else {
5825
+ const jsdoc_type = returnType === undefined ? type : `(arg0: ${type}) => ${returnType}`;
5826
+ addJsDocTypeToFunction(surround, insert, fn.node, jsdoc_type);
5613
5827
  }
5614
5828
  }
5615
5829
  }
5830
+ function addJsDocTypeToVariable(surround, insert, node, type) {
5831
+ if (!node.initializer) {
5832
+ return;
5833
+ }
5834
+ insert(node.initializer.getStart(), surround(`/** @type {${type}} */ (`));
5835
+ insert(node.initializer.getEnd(), surround(`)`));
5836
+ }
5837
+ function addJsDocSatisfiesToVariable(surround, insert, node, type) {
5838
+ if (!node.initializer) {
5839
+ return;
5840
+ }
5841
+ insert(node.initializer.getStart(), surround(`/** @satisfies {${type}} */ (`));
5842
+ insert(node.initializer.getEnd(), surround(`)`));
5843
+ }
5844
+ function addJsDocTypeToFunction(surround, insert, node, type) {
5845
+ insert(node.getStart(), surround(`/** @type {${type}} */ `));
5846
+ }
5847
+ function addJsDocParamToFunction(surround, insert, ts, node, type) {
5848
+ const parameter = node.parameters[0];
5849
+ if (!parameter) {
5850
+ return;
5851
+ }
5852
+ const parameter_name = ts.isIdentifier(parameter.name) ? parameter.name.text : 'arg0';
5853
+ insert(node.getStart(), surround(`/** @param {${type}} ${parameter_name} */ `));
5854
+ }
5616
5855
  function insertCode(addedCode, pos, inserted) {
5617
5856
  var _a, _b, _c, _d;
5618
5857
  const insertionIdx = addedCode.findIndex((c) => c.originalPos > pos);
@@ -7666,7 +7905,7 @@ class InterfacesAndTypes {
7666
7905
  }
7667
7906
  }
7668
7907
 
7669
- function processInstanceScriptContent(str, script, events, implicitStoreValues, mode, moduleAst, isTSFile, basename, isSvelte5Plus, isRunes, emitJsDoc) {
7908
+ function processInstanceScriptContent(str, script, events, implicitStoreValues, mode, moduleAst, isTSFile, basename, isSvelte5Plus, isRunes, emitJsDoc, rewriteExternalImports) {
7670
7909
  const htmlx = str.original;
7671
7910
  const scriptContent = htmlx.substring(script.content.start, script.content.end);
7672
7911
  const tsAst = ts.createSourceFile('component.ts.svelte', scriptContent, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
@@ -7778,6 +8017,11 @@ function processInstanceScriptContent(str, script, events, implicitStoreValues,
7778
8017
  const walk = (node, parent) => {
7779
8018
  var _a, _b, _c;
7780
8019
  const onLeaveCallbacks = [];
8020
+ if (rewriteExternalImports) {
8021
+ rewriteExternalImportsInNode(ts, node, rewriteExternalImports, (specifier, rewrite) => {
8022
+ str.overwrite(specifier.getStart(tsAst) + astOffset + 1, specifier.getEnd() + astOffset - 1, rewrite.rewritten);
8023
+ });
8024
+ }
7781
8025
  if (parent === tsAst) {
7782
8026
  exportedNames.hoistableInterfaces.analyzeInstanceScriptNode(node);
7783
8027
  }
@@ -7961,7 +8205,7 @@ function createModuleAst(str, script) {
7961
8205
  const astOffset = script.content.start;
7962
8206
  return { htmlx, tsAst, astOffset };
7963
8207
  }
7964
- function processModuleScriptTag(str, script, implicitStoreValues, moduleAst) {
8208
+ function processModuleScriptTag(str, script, implicitStoreValues, moduleAst, rewriteExternalImports) {
7965
8209
  const { htmlx, tsAst, astOffset } = moduleAst;
7966
8210
  const generics = new Generics(str, astOffset, script);
7967
8211
  if (generics.genericsAttr) {
@@ -7969,6 +8213,11 @@ function processModuleScriptTag(str, script, implicitStoreValues, moduleAst) {
7969
8213
  throwError(start, start + 8, 'The generics attribute is only allowed on the instance script', str.original);
7970
8214
  }
7971
8215
  const walk = (node) => {
8216
+ if (rewriteExternalImports) {
8217
+ rewriteExternalImportsInNode(ts, node, rewriteExternalImports, (specifier, rewrite) => {
8218
+ str.overwrite(specifier.getStart(tsAst) + astOffset + 1, specifier.getEnd() + astOffset - 1, rewrite.rewritten);
8219
+ });
8220
+ }
7972
8221
  resolveImplicitStoreValue(node, implicitStoreValues, str, astOffset);
7973
8222
  generics.throwIfIsGeneric(node);
7974
8223
  throwIfIs$$EventsDeclaration(node, str, astOffset);
@@ -8032,6 +8281,13 @@ function svelte2tsx(svelte, options = { parse }) {
8032
8281
  options.mode = options.mode || 'ts';
8033
8282
  options.version = options.version || VERSION;
8034
8283
  const str = new MagicString(svelte);
8284
+ const rewriteExternalImportsOptions = options.rewriteExternalImports && options.filename
8285
+ ? {
8286
+ sourcePath: options.filename,
8287
+ generatedPath: options.rewriteExternalImports.generatedPath,
8288
+ workspacePath: options.rewriteExternalImports.workspacePath
8289
+ }
8290
+ : undefined;
8035
8291
  const basename = path__default.basename(options.filename || '');
8036
8292
  const svelte5Plus = Number(options.version[0]) > 4;
8037
8293
  const isTsFile = options === null || options === void 0 ? void 0 : options.isTsFile;
@@ -8039,7 +8295,8 @@ function svelte2tsx(svelte, options = { parse }) {
8039
8295
  // process the htmlx as a svelte template
8040
8296
  let { htmlAst, moduleScriptTag, scriptTag, rootSnippets, slots, uses$$props, uses$$slots, uses$$restProps, events, componentDocumentation, resolvedStores, usesAccessors, isRunes } = processSvelteTemplate(str, options.parse || parse, {
8041
8297
  ...options,
8042
- svelte5Plus
8298
+ svelte5Plus,
8299
+ rewriteExternalImports: rewriteExternalImportsOptions
8043
8300
  });
8044
8301
  /* Rearrange the script tags so that module is first, and instance second followed finally by the template
8045
8302
  * This is a bit convoluted due to some trouble I had with magic string. A simple str.move(start,end,0) for each script wasn't enough
@@ -8073,7 +8330,7 @@ function svelte2tsx(svelte, options = { parse }) {
8073
8330
  if (scriptTag.start != instanceScriptTarget) {
8074
8331
  str.move(scriptTag.start, scriptTag.end, instanceScriptTarget);
8075
8332
  }
8076
- const res = processInstanceScriptContent(str, scriptTag, events, implicitStoreValues, options.mode, moduleAst, isTsFile, basename, svelte5Plus, isRunes, emitJsDoc);
8333
+ const res = processInstanceScriptContent(str, scriptTag, events, implicitStoreValues, options.mode, moduleAst, isTsFile, basename, svelte5Plus, isRunes, emitJsDoc, rewriteExternalImportsOptions);
8077
8334
  uses$$props = uses$$props || res.uses$$props;
8078
8335
  uses$$restProps = uses$$restProps || res.uses$$restProps;
8079
8336
  uses$$slots = uses$$slots || res.uses$$slots;
@@ -8107,7 +8364,7 @@ function svelte2tsx(svelte, options = { parse }) {
8107
8364
  });
8108
8365
  // we need to process the module script after the instance script has moved otherwise we get warnings about moving edited items
8109
8366
  if (moduleScriptTag) {
8110
- processModuleScriptTag(str, moduleScriptTag, new ImplicitStoreValues(implicitStoreValues.getAccessedStores(), renderFunctionStart, svelte5Plus, scriptTag || options.mode === 'ts' ? undefined : (input) => `</>;${input}<>`), moduleAst);
8367
+ processModuleScriptTag(str, moduleScriptTag, new ImplicitStoreValues(implicitStoreValues.getAccessedStores(), renderFunctionStart, svelte5Plus, scriptTag || options.mode === 'ts' ? undefined : (input) => `</>;${input}<>`), moduleAst, rewriteExternalImportsOptions);
8111
8368
  if (!scriptTag) {
8112
8369
  moduleAst.tsAst.forEachChild((node) => exportedNames.hoistableInterfaces.analyzeModuleScriptNode(node));
8113
8370
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte2tsx",
3
- "version": "0.7.48",
3
+ "version": "0.7.50",
4
4
  "description": "Convert Svelte components to TSX for type checking",
5
5
  "author": "The Svelte Community",
6
6
  "license": "MIT",