Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(svelte2tsx) export class, const and function #328

Merged
merged 2 commits into from
Jul 25, 2020
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 @@ -152,7 +152,7 @@ export class RenameProviderImpl implements RenameProvider {
// First find out if it's really the "rename prop inside component with that prop" case
// Use original document for that because only there the `export` is present.
const regex = new RegExp(
`export\\s+(const|let)\\s+${this.getVariableAtPosition(
`export\\s+let\\s+${this.getVariableAtPosition(
tsDoc,
fragment,
lang,
Expand Down
2 changes: 2 additions & 0 deletions packages/svelte2tsx/.editorconfig
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
[test/**/*.{tsx,jsx,html}]
trim_trailing_whitespace = false
[test/**/*.html]
insert_final_newline = false
25 changes: 25 additions & 0 deletions packages/svelte2tsx/src/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import MagicString from 'magic-string';
import { Node } from 'estree-walker';

export type ExportedNames = Map<
string,
{
type?: string;
identifierText?: string;
required?: boolean;
}
>;

export interface InstanceScriptProcessResult {
exportedNames: ExportedNames;
uses$$props: boolean;
uses$$restProps: boolean;
getters: Set<string>;
}

export interface CreateRenderFunctionPara extends InstanceScriptProcessResult {
str: MagicString;
scriptTag: Node;
scriptDestination: number;
slots: Map<string, Map<string, string>>;
}
9 changes: 9 additions & 0 deletions packages/svelte2tsx/src/nodes/exportgetters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const createClassGetter = (name: string) =>
`\n${' '.repeat(4)}get ${name}() { return render().getters.${name} }`;
export const createClassGetters = (names: Set<string>) => {
return Array.from(names).map(createClassGetter).join('');
};
export function createRenderFunctionGetterStr(getters: Set<string>) {
const properties = Array.from(getters).map((name) => `${name}: ${name}`);
return `{${properties.join(', ')}}`;
}
106 changes: 61 additions & 45 deletions packages/svelte2tsx/src/svelte2tsx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { parseHtmlx } from './htmlxparser';
import { convertHtmlxToJsx } from './htmlxtojsx';
import { Node } from 'estree-walker';
import * as ts from 'typescript';
import { findExortKeyword } from './utils/tsAst';
import { ExportedNames, InstanceScriptProcessResult, CreateRenderFunctionPara } from './interfaces';
import { createRenderFunctionGetterStr, createClassGetters } from './nodes/exportgetters';

function AttributeValueAsJsExpression(htmlx: string, attr: Node): string {
if (attr.value.length == 0) return "''"; //wut?
Expand Down Expand Up @@ -364,20 +367,6 @@ function processSvelteTemplate(str: MagicString): TemplateProcessResult {
};
}

type ExportedNames = Map<
string,
{
type?: string;
identifierText?: string;
required?: boolean;
}
>;

type InstanceScriptProcessResult = {
exportedNames: ExportedNames;
uses$$props: boolean;
uses$$restProps: boolean;
};

function processInstanceScriptContent(str: MagicString, script: Node): InstanceScriptProcessResult {
const htmlx = str.original;
Expand All @@ -391,6 +380,7 @@ function processInstanceScriptContent(str: MagicString, script: Node): InstanceS
);
const astOffset = script.content.start;
const exportedNames: ExportedNames = new Map();
const getters = new Set<string>();

const implicitTopLevelNames: Map<string, number> = new Map();
let uses$$props = false;
Expand Down Expand Up @@ -431,6 +421,12 @@ function processInstanceScriptContent(str: MagicString, script: Node): InstanceS
exportedNames.set(name.text, {});
}
};
const addGetter = (node: ts.Identifier) => {
if (!node) {
return;
}
getters.add(node.text);
};

const removeExport = (start: number, end: number) => {
const exportStart = str.original.indexOf('export', start + astOffset);
Expand All @@ -439,10 +435,6 @@ function processInstanceScriptContent(str: MagicString, script: Node): InstanceS
};

const propTypeAssertToUserDefined = (node: ts.VariableDeclarationList) => {
if (node.flags !== ts.NodeFlags.Let) {
return;
}

const hasInitializers = node.declarations.filter((declaration) => declaration.initializer);
const handleTypeAssertion = (declaration: ts.VariableDeclaration) => {
const identifier = declaration.name;
Expand Down Expand Up @@ -669,31 +661,46 @@ function processInstanceScriptContent(str: MagicString, script: Node): InstanceS
const onLeaveCallbacks: onLeaveCallback[] = [];

if (ts.isVariableStatement(node)) {
const exportModifier = node.modifiers
? node.modifiers.find((x) => x.kind == ts.SyntaxKind.ExportKeyword)
: null;
const exportModifier = findExortKeyword(node);
if (exportModifier) {
handleExportedVariableDeclarationList(node.declarationList);
propTypeAssertToUserDefined(node.declarationList);
const isLet = node.declarationList.flags === ts.NodeFlags.Let;
const isConst = node.declarationList.flags === ts.NodeFlags.Const;

if (isLet) {
handleExportedVariableDeclarationList(node.declarationList);
propTypeAssertToUserDefined(node.declarationList);
} else if (isConst) {
node.declarationList.forEachChild((n) => {
if (ts.isVariableDeclaration(n) && ts.isIdentifier(n.name)) {
addGetter(n.name);
}
});
}
removeExport(exportModifier.getStart(), exportModifier.end);
}
}

if (ts.isFunctionDeclaration(node)) {
if (node.modifiers) {
const exportModifier = node.modifiers.find(
(x) => x.kind == ts.SyntaxKind.ExportKeyword,
);
const exportModifier = findExortKeyword(node);
if (exportModifier) {
addExport(node.name);
removeExport(exportModifier.getStart(), exportModifier.end);
addGetter(node.name);
}
}

pushScope();
onLeaveCallbacks.push(() => popScope());
}

if (ts.isClassDeclaration(node)) {
const exportModifier = findExortKeyword(node);
if (exportModifier) {
removeExport(exportModifier.getStart(), exportModifier.end);
addGetter(node.name);
}
}

if (ts.isBlock(node)) {
pushScope();
onLeaveCallbacks.push(() => popScope());
Expand Down Expand Up @@ -803,6 +810,7 @@ function processInstanceScriptContent(str: MagicString, script: Node): InstanceS
exportedNames,
uses$$props,
uses$$restProps,
getters,
};
}

Expand All @@ -825,6 +833,7 @@ function addComponentExport(
uses$$propsOr$$restProps: boolean,
strictMode: boolean,
isTsFile: boolean,
getters: Set<string>,
/** A named export allows for TSDoc-compatible docstrings */
className?: string,
componentDocumentation?: string | null,
Expand All @@ -841,10 +850,12 @@ function addComponentExport(

const doc = formatComponentDocumentation(componentDocumentation);

// eslint-disable-next-line max-len
const statement = `\n\n${doc}export default class ${
className ? `${className} ` : ''
}{\n $$prop_def = ${propDef}\n $$slot_def = render().slots\n}`;
const statement =
`\n\n${doc}export default class ${
className ? `${className} ` : ''
}{\n $$prop_def = ${propDef}\n $$slot_def = render().slots` +
createClassGetters(getters) +
'\n}';

str.append(statement);
}
Expand Down Expand Up @@ -899,15 +910,16 @@ function processModuleScriptTag(str: MagicString, script: Node) {
str.overwrite(scriptEndTagStart, script.end, ';<>');
}

function createRenderFunction(
str: MagicString,
scriptTag: Node,
scriptDestination: number,
slots: Map<string, Map<string, string>>,
exportedNames: ExportedNames,
uses$$props: boolean,
uses$$restProps: boolean,
) {
function createRenderFunction({
str,
scriptTag,
scriptDestination,
slots,
getters,
exportedNames,
uses$$props,
uses$$restProps
}: CreateRenderFunctionPara) {
const htmlx = str.original;
let propsDecl = '';

Expand Down Expand Up @@ -946,7 +958,7 @@ function createRenderFunction(

const returnString = `\nreturn { props: ${createPropsStr(
exportedNames,
)}, slots: ${slotsAsDef} }}`;
)}, slots: ${slotsAsDef}, getters: ${createRenderFunctionGetterStr(getters)} }}`;
str.append(returnString);
}

Expand Down Expand Up @@ -1008,27 +1020,30 @@ export function svelte2tsx(svelte: string, options?: { filename?: string; strict

//move the instance script and process the content
let exportedNames: ExportedNames = new Map();
let getters = new Set<string>();
if (scriptTag) {
//ensure it is between the module script and the rest of the template (the variables need to be declared before the jsx template)
if (scriptTag.start != instanceScriptTarget) {
str.move(scriptTag.start, scriptTag.end, instanceScriptTarget);
}
const res = processInstanceScriptContent(str, scriptTag);
exportedNames = res.exportedNames;
uses$$props = uses$$props || res.uses$$props;
uses$$restProps = uses$$restProps || res.uses$$restProps;

({ exportedNames, getters } = res);
}

//wrap the script tag and template content in a function returning the slot and exports
createRenderFunction(
createRenderFunction({
str,
scriptTag,
instanceScriptTarget,
scriptDestination: instanceScriptTarget,
slots,
getters,
exportedNames,
uses$$props,
uses$$restProps,
);
});

// we need to process the module script after the instance script has moved otherwise we get warnings about moving edited items
if (moduleScriptTag) {
Expand All @@ -1042,6 +1057,7 @@ export function svelte2tsx(svelte: string, options?: { filename?: string; strict
uses$$props || uses$$restProps,
!!options?.strictMode,
isTsFile(scriptTag, moduleScriptTag),
getters,
className,
componentDocumentation,
);
Expand Down
5 changes: 5 additions & 0 deletions packages/svelte2tsx/src/utils/tsAst.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import ts from 'typescript';

export function findExortKeyword(node: ts.Node) {
return node.modifiers?.find((x) => x.kind == ts.SyntaxKind.ExportKeyword);
}
2 changes: 0 additions & 2 deletions packages/svelte2tsx/test/sourcemaps/.editorconfig

This file was deleted.

2 changes: 1 addition & 1 deletion packages/svelte2tsx/test/sourcemaps/event-binding.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
1==== 2==================
<button onclick={__sveltets_store_get(check) ? method1 : method2} >Bla</button></>
3==== 4==================
return { props: {}, slots: {} }}
return { props: {}, slots: {}, getters: {} }}

export default class {
$$prop_def = __sveltets_partial(render().props)
Expand Down
2 changes: 1 addition & 1 deletion packages/svelte2tsx/test/sourcemaps/let.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
;
<>
</>
return { props: {}, slots: {} }}
return { props: {}, slots: {}, getters: {} }}

export default class {
$$prop_def = __sveltets_partial(render().props)
Expand Down
2 changes: 1 addition & 1 deletion packages/svelte2tsx/test/sourcemaps/repl.html
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@
</>}}}
</div>
</>
return { props: {slug: slug , chapter: chapter}, slots: {} }}
return { props: {slug: slug , chapter: chapter}, slots: {}, getters: {} }}

export default class {
$$prop_def = __sveltets_partial(render().props)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
let [a,b,c] = [1,2,3];
;
<></>
return { props: {a: a , b: b , c: c}, slots: {} }}
return { props: {a: a , b: b , c: c}, slots: {}, getters: {} }}

export default class Input__SvelteComponent_ {
$$prop_def = __sveltets_partial(render().props)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<></>;function render() {
__sveltets_store_get(var);
<></>
return { props: {}, slots: {} }}
return { props: {}, slots: {}, getters: {} }}

export default class Input__SvelteComponent_ {
$$prop_def = __sveltets_partial(render().props)
$$slot_def = render().slots
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<></>;function render() {
__sveltets_store_get(var);
<></>
return { props: {}, slots: {} }}
return { props: {}, slots: {}, getters: {} }}

export default class Input__SvelteComponent_ {
$$prop_def = __sveltets_partial(render().props)
$$slot_def = render().slots
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function render() {
</>; _$$p.then((data) => {<>
{data}
</>})}}</>
return { props: {}, slots: {} }}
return { props: {}, slots: {}, getters: {} }}

export default class Input__SvelteComponent_ {
$$prop_def = __sveltets_partial(render().props)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<></>;function render() {
<><input id="dom-input" type="radio" {...__sveltets_any(__sveltets_store_get(compile_options).generate)} value="dom"/></>
return { props: {}, slots: {} }}
return { props: {}, slots: {}, getters: {} }}

export default class Input__SvelteComponent_ {
$$prop_def = __sveltets_partial(render().props)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@
<input type="range" value={selected.r} oninput={adjust}/>
</div>
</>}}}</>
return { props: {}, slots: {} }}
return { props: {}, slots: {}, getters: {} }}

export default class Input__SvelteComponent_ {
$$prop_def = __sveltets_partial(render().props)
$$slot_def = render().slots
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
<div>
<slot a={b}>Hello</slot>
</div></>
return { props: {}, slots: {default: {a:b}} }}
return { props: {}, slots: {default: {a:b}}, getters: {} }}

export default class Input__SvelteComponent_ {
$$prop_def = __sveltets_partial(render().props)
$$slot_def = render().slots
}
}
Loading