Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,9 @@ export async function getComponentAtPosition(
export function isInGeneratedCode(text: string, start: number, end: number) {
const lineStart = text.lastIndexOf('\n', start);
const lineEnd = text.indexOf('\n', end);
return (
text.substring(lineStart, start).includes('/*Ωignore_startΩ*/') &&
text.substring(end, lineEnd).includes('/*Ωignore_endΩ*/')
);
const lastStart = text.substring(lineStart, start).lastIndexOf('/*Ωignore_startΩ*/');
const lastEnd = text.substring(lineStart, start).lastIndexOf('/*Ωignore_endΩ*/');
return lastStart > lastEnd && text.substring(end, lineEnd).includes('/*Ωignore_endΩ*/');
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ describe('RenameProvider', () => {
const renameDoc5 = await openDoc('rename5.svelte');
const renameDoc6 = await openDoc('rename6.svelte');
const renameDocIgnoreGenerated = await openDoc('rename-ignore-generated.svelte');
const renameDocSlotEventsImporter = await openDoc('rename-slot-events-importer.svelte');
const renameDocPropWithSlotEvents = await openDoc('rename-prop-with-slot-events.svelte');
return {
provider,
renameDoc1,
Expand All @@ -45,6 +47,8 @@ describe('RenameProvider', () => {
renameDoc5,
renameDoc6,
renameDocIgnoreGenerated,
renameDocSlotEventsImporter,
renameDocPropWithSlotEvents,
docManager
};

Expand Down Expand Up @@ -526,4 +530,61 @@ describe('RenameProvider', () => {
}
});
});

it('rename prop correctly when events/slots present', async () => {
const { provider, renameDocPropWithSlotEvents } = await setup();
const result = await provider.rename(
renameDocPropWithSlotEvents,
Position.create(3, 15),
'newName'
);

assert.deepStrictEqual(result, {
changes: {
[getUri('rename-prop-with-slot-events.svelte')]: [
{
newText: 'newName',
range: {
end: {
character: 17,
line: 3
},
start: {
character: 13,
line: 3
}
}
},
{
newText: 'newName',
range: {
end: {
character: 17,
line: 8
},
start: {
character: 13,
line: 8
}
}
}
],
[getUri('rename-slot-events-importer.svelte')]: [
{
newText: 'newName',
range: {
end: {
character: 7,
line: 4
},
start: {
character: 3,
line: 4
}
}
}
]
}
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script>
import { createEventDispatcher } from 'svelte';
export let prop;
const dispatch = createEventDispatcher();
</script>

<button on:click={() => dispatch('foo')}>click</button>
<slot aSlot={prop} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script>
import A from './rename-prop-with-slot-events.svelte';
</script>

<A prop={1} on:click={e => e} let:aSlot>{aSlot}</A>
2 changes: 1 addition & 1 deletion packages/svelte2tsx/src/htmlxtojsx/nodes/await.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function handleAwait(
let ifCondition = ifScope.getFullCondition();
ifCondition = ifCondition ? surroundWithIgnoreComments(`if(${ifCondition}) {`) : '';
templateScopeManager.awaitEnter(awaitBlock);
const constRedeclares = ifScope.getConstsToRedeclare();
const constRedeclares = ifScope.getConstDeclaration();
str.overwrite(
awaitBlock.start,
awaitBlock.expression.start,
Expand Down
2 changes: 1 addition & 1 deletion packages/svelte2tsx/src/htmlxtojsx/nodes/each.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function handleEach(
): void {
// {#each items as item,i (key)} ->
// {__sveltets_each(items, (item,i) => (key) && (possible if expression &&) <>
const constRedeclares = ifScope.getConstsToRedeclare();
const constRedeclares = ifScope.getConstDeclaration();
const prefix = constRedeclares ? `{() => {${constRedeclares}() => ` : '';
str.overwrite(eachBlock.start, eachBlock.expression.start, `${prefix}{__sveltets_each(`);
str.overwrite(eachBlock.expression.end, eachBlock.context.start, ', (');
Expand Down
4 changes: 2 additions & 2 deletions packages/svelte2tsx/src/htmlxtojsx/nodes/event-handler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import MagicString from 'magic-string';
import { getTypeForComponent, isQuote } from '../utils/node-utils';
import { getInstanceType, isQuote } from '../utils/node-utils';
import { BaseDirective, BaseNode } from '../../interfaces';

/**
Expand Down Expand Up @@ -36,7 +36,7 @@ export function handleEventHandler(
if (attr.expression) {
const on = 'on';
//for handler assignment, we change it to call to our __sveltets_ensureFunction
str.appendRight(attr.start, `{__sveltets_instanceOf(${getTypeForComponent(parent)}).$`);
str.appendRight(attr.start, `{${getInstanceType(parent, str.original)}.$`);
const eventNameIndex = htmlx.indexOf(':', attr.start) + 1;
str.overwrite(htmlx.indexOf(on, attr.start) + on.length, eventNameIndex, "('");
const eventEnd = htmlx.lastIndexOf('=', attr.expression.start);
Expand Down
18 changes: 13 additions & 5 deletions packages/svelte2tsx/src/htmlxtojsx/nodes/if-scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,13 +323,21 @@ export class IfScope {
* in the conditions which would be overwritten by the scope
* (because they declare a variable with the same name, therefore shadowing the outer variable).
*/
getConstsToRedeclare(): string {
const replacements = this.getNamesToRedeclare()
.map((identifier) => `${this.replacementPrefix + identifier}=${identifier}`)
.join(',');
getConstDeclaration(): string {
const replacements = this.getConstsToRedeclare().join(',');
return replacements ? surroundWithIgnoreComments(`const ${replacements};`) : '';
}

/**
* Like `getConstsRedaclarationString`, but only returns a list of redaclaration-string without
* merging the result with `const` and a ignore comments surround.
*/
getConstsToRedeclare(): string[] {
return this.getNamesToRedeclare().map(
(identifier) => `${this.replacementPrefix + identifier}=${identifier}`
);
}

/**
* Returns true if given identifier is referenced in this IfScope or a parent scope.
*/
Expand All @@ -353,7 +361,7 @@ export class IfScope {
/**
* Contains a list of identifiers which would be overwritten by the child template scope.
*/
private getNamesToRedeclare() {
getNamesToRedeclare() {
return [...this.scope.value.inits.keys()].filter((init) => {
let parent = this.scope.value.parent;
while (parent && parent !== this.ownScope) {
Expand Down
69 changes: 64 additions & 5 deletions packages/svelte2tsx/src/htmlxtojsx/nodes/slot.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
import MagicString from 'magic-string';
import { beforeStart } from '../utils/node-utils';
import { getSingleSlotDef } from '../../svelte2tsx/nodes/slot';
import {
beforeStart,
getInstanceType,
getInstanceTypeForDefaultSlot,
PropsShadowedByLet
} from '../utils/node-utils';
import { IfScope } from './if-scope';
import { TemplateScope } from '../nodes/template-scope';
import { BaseNode } from '../../interfaces';
import { surroundWithIgnoreComments } from '../../utils/ignore';

const shadowedPropsSymbol = Symbol('shadowedProps');

interface ComponentNode extends BaseNode {
// Not pretty, but it works, and because it's a symbol, estree-walker will ignore it
[shadowedPropsSymbol]?: PropsShadowedByLet[];
}

export function handleSlot(
htmlx: string,
str: MagicString,
slotEl: BaseNode,
component: BaseNode,
component: ComponentNode,
slotName: string,
ifScope: IfScope,
templateScope: TemplateScope
Expand Down Expand Up @@ -57,16 +69,63 @@ export function handleSlot(
return;
}

const constRedeclares = ifScope.getConstsToRedeclare();
const { singleSlotDef, constRedeclares } = getSingleSlotDefAndConstsRedeclaration(
component,
slotName,
str.original,
ifScope,
slotElIsComponent
);
const prefix = constRedeclares ? `() => {${constRedeclares}` : '';
str.appendLeft(slotDefInsertionPoint, `{${prefix}() => { let {`);
str.appendRight(
slotDefInsertionPoint,
`} = ${getSingleSlotDef(component, slotName)}` + `;${ifScope.addPossibleIfCondition()}<>`
`} = ${singleSlotDef}` + `;${ifScope.addPossibleIfCondition()}<>`
);

const closeSlotDefInsertionPoint = slotElIsComponent
? htmlx.lastIndexOf('<', slotEl.end - 1)
: slotEl.end;
str.appendLeft(closeSlotDefInsertionPoint, `</>}}${constRedeclares ? '}' : ''}`);
}

function getSingleSlotDefAndConstsRedeclaration(
componentNode: ComponentNode,
slotName: string,
originalStr: string,
ifScope: IfScope,
findAndRedeclareShadowedProps: boolean
) {
if (findAndRedeclareShadowedProps) {
const replacement = 'Ψ';
const { str, shadowedProps } = getInstanceTypeForDefaultSlot(
componentNode,
originalStr,
replacement
);
componentNode[shadowedPropsSymbol] = shadowedProps;
return {
singleSlotDef: `${str}.$$slot_def['${slotName}']`,
constRedeclares: getConstsToRedeclare(ifScope, shadowedProps)
};
} else {
const str = getInstanceType(
componentNode,
originalStr,
componentNode[shadowedPropsSymbol] || []
);
return {
singleSlotDef: `${str}.$$slot_def['${slotName}']`,
constRedeclares: ifScope.getConstDeclaration()
};
}
}

function getConstsToRedeclare(ifScope: IfScope, shadowedProps: PropsShadowedByLet[]) {
const ifScopeRedeclarations = ifScope.getConstsToRedeclare();
const letRedeclarations = shadowedProps.map(
({ value, replacement }) => `${replacement}=${value}`
);
const replacements = [...ifScopeRedeclarations, ...letRedeclarations].join(',');
return replacements ? surroundWithIgnoreComments(`const ${replacements};`) : '';
}
Loading