fix: error at compile time on unsupported TypeScript language features (#12982)

part of #11502 - to close it completely, we also need to look at using and possibly implement heuristics within bundler plugins to give more details

---------

Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
pull/13113/head
Rich Harris 1 year ago committed by GitHub
parent 81b32d8b5a
commit 194570dc70
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: error at compile time on unsupported TypeScript language features

@ -159,3 +159,7 @@
> Cannot reference store value outside a `.svelte` file
Using a `$` prefix to refer to the value of a store is only possible inside `.svelte` files, where Svelte can automatically create subscriptions when a component is mounted and unsubscribe when the component is unmounted. Consider migrating to runes instead.
## typescript_invalid_feature
> TypeScript language features like %feature% are not natively supported, and their use is generally discouraged. Outside of `<script>` tags, these features are not supported. For use within `<script>` tags, you will need to use a preprocessor to convert it to JavaScript before it gets passed to the Svelte compiler. If you are using `vitePreprocess`, make sure to specifically enable preprocessing script tags (`vitePreprocess({ script: true })`)

@ -434,6 +434,16 @@ export function store_invalid_subscription_module(node) {
e(node, "store_invalid_subscription_module", "Cannot reference store value outside a `.svelte` file");
}
/**
* TypeScript language features like %feature% are not natively supported, and their use is generally discouraged. Outside of `<script>` tags, these features are not supported. For use within `<script>` tags, you will need to use a preprocessor to convert it to JavaScript before it gets passed to the Svelte compiler. If you are using `vitePreprocess`, make sure to specifically enable preprocessing script tags (`vitePreprocess({ script: true })`)
* @param {null | number | NodeLike} node
* @param {string} feature
* @returns {never}
*/
export function typescript_invalid_feature(node, feature) {
e(node, "typescript_invalid_feature", `TypeScript language features like ${feature} are not natively supported, and their use is generally discouraged. Outside of \`<script>\` tags, these features are not supported. For use within \`<script>\` tags, you will need to use a preprocessor to convert it to JavaScript before it gets passed to the Svelte compiler. If you are using \`vitePreprocess\`, make sure to specifically enable preprocessing script tags (\`vitePreprocess({ script: true })\`)`);
}
/**
* Declaration cannot be empty
* @param {null | number | NodeLike} node

@ -2,6 +2,7 @@
/** @import { FunctionExpression, FunctionDeclaration } from 'estree' */
import { walk } from 'zimmerframe';
import * as b from '../../utils/builders.js';
import * as e from '../../errors.js';
/**
* @param {FunctionExpression | FunctionDeclaration} node
@ -16,6 +17,9 @@ function remove_this_param(node, context) {
/** @type {Visitors<any, null>} */
const visitors = {
Decorator(node) {
e.typescript_invalid_feature(node, 'decorators (related TSC proposal is not stage 4 yet)');
},
ImportDeclaration(node) {
if (node.importKind === 'type') return b.empty;
@ -52,6 +56,14 @@ const visitors = {
if (node.exportKind === 'type') return b.empty;
return node;
},
PropertyDefinition(node) {
if (node.accessor) {
e.typescript_invalid_feature(
node,
'accessor fields (related TSC proposal is not stage 4 yet)'
);
}
},
TSAsExpression(node, context) {
return context.visit(node.expression);
},
@ -73,10 +85,13 @@ const visitors = {
TSTypeParameterInstantiation() {
return b.empty;
},
TSEnumDeclaration() {
return b.empty;
TSEnumDeclaration(node) {
e.typescript_invalid_feature(node, 'enums');
},
TSParameterProperty(node) {
TSParameterProperty(node, context) {
if (node.accessibility && context.path.at(-2)?.kind === 'constructor') {
e.typescript_invalid_feature(node, 'accessibility modifiers on constructor parameters');
}
return node.parameter;
},
Identifier(node) {
@ -89,7 +104,33 @@ const visitors = {
return node;
},
FunctionExpression: remove_this_param,
FunctionDeclaration: remove_this_param
FunctionDeclaration: remove_this_param,
TSDeclareFunction() {
return b.empty;
},
ClassDeclaration(node, context) {
if (node.declare) {
return b.empty;
}
return context.next();
},
VariableDeclaration(node, context) {
if (node.declare) {
return b.empty;
}
return context.next();
},
TSModuleDeclaration(node, context) {
if (!node.body) return b.empty;
// namespaces can contain non-type nodes
const cleaned = /** @type {any[]} */ (node.body.body).map((entry) => context.visit(entry));
if (cleaned.some((entry) => entry !== b.empty)) {
e.typescript_invalid_feature(node, 'namespaces with non-type nodes');
}
return b.empty;
}
};
/**

@ -9,7 +9,18 @@
}
class Foo {
constructor(public readonly name: string) {}
constructor(readonly name: string) {}
}
declare const declared_const: number;
declare function declared_fn(): void;
declare class declared_class {
foo: number;
}
declare module 'foobar' {}
namespace SomeNamespace {
export type Foo = true
}
export type { Hello };

@ -0,0 +1,14 @@
[
{
"code": "typescript_invalid_feature",
"message": "TypeScript language features like accessor fields (related TSC proposal is not stage 4 yet) are not natively supported, and their use is generally discouraged. Outside of `<script>` tags, these features are not supported. For use within `<script>` tags, you will need to use a preprocessor to convert it to JavaScript before it gets passed to the Svelte compiler. If you are using `vitePreprocess`, make sure to specifically enable preprocessing script tags (`vitePreprocess({ script: true })`)",
"start": {
"line": 3,
"column": 2
},
"end": {
"line": 3,
"column": 17
}
}
]

@ -0,0 +1,5 @@
<script lang="ts">
class Foo {
accessor y = 1;
}
</script>

@ -0,0 +1,14 @@
[
{
"code": "typescript_invalid_feature",
"message": "TypeScript language features like decorators (related TSC proposal is not stage 4 yet) are not natively supported, and their use is generally discouraged. Outside of `<script>` tags, these features are not supported. For use within `<script>` tags, you will need to use a preprocessor to convert it to JavaScript before it gets passed to the Svelte compiler. If you are using `vitePreprocess`, make sure to specifically enable preprocessing script tags (`vitePreprocess({ script: true })`)",
"start": {
"line": 2,
"column": 4
},
"end": {
"line": 2,
"column": 10
}
}
]

@ -0,0 +1,4 @@
<script lang="ts">
@foo()
class Foo {}
</script>

@ -0,0 +1,14 @@
[
{
"code": "typescript_invalid_feature",
"message": "TypeScript language features like enums are not natively supported, and their use is generally discouraged. Outside of `<script>` tags, these features are not supported. For use within `<script>` tags, you will need to use a preprocessor to convert it to JavaScript before it gets passed to the Svelte compiler. If you are using `vitePreprocess`, make sure to specifically enable preprocessing script tags (`vitePreprocess({ script: true })`)",
"start": {
"line": 2,
"column": 1
},
"end": {
"line": 4,
"column": 2
}
}
]

@ -0,0 +1,5 @@
<script lang="ts">
enum Foo {
bar = 1
}
</script>

@ -0,0 +1,14 @@
[
{
"code": "typescript_invalid_feature",
"message": "TypeScript language features like accessibility modifiers on constructor parameters are not natively supported, and their use is generally discouraged. Outside of `<script>` tags, these features are not supported. For use within `<script>` tags, you will need to use a preprocessor to convert it to JavaScript before it gets passed to the Svelte compiler. If you are using `vitePreprocess`, make sure to specifically enable preprocessing script tags (`vitePreprocess({ script: true })`)",
"start": {
"line": 3,
"column": 14
},
"end": {
"line": 3,
"column": 31
}
}
]

@ -0,0 +1,5 @@
<script lang="ts">
class Foo {
constructor(private x: number) {}
}
</script>

@ -0,0 +1,14 @@
[
{
"code": "typescript_invalid_feature",
"message": "TypeScript language features like namespaces with non-type nodes are not natively supported, and their use is generally discouraged. Outside of `<script>` tags, these features are not supported. For use within `<script>` tags, you will need to use a preprocessor to convert it to JavaScript before it gets passed to the Svelte compiler. If you are using `vitePreprocess`, make sure to specifically enable preprocessing script tags (`vitePreprocess({ script: true })`)",
"start": {
"line": 2,
"column": 1
},
"end": {
"line": 4,
"column": 2
}
}
]

@ -0,0 +1,5 @@
<script module lang="ts">
namespace SomeNamespace {
export const foo = true;
}
</script>

@ -53,10 +53,20 @@ const { test, run } = suite<ValidatorTest>(async (config, cwd) => {
const expected = expected_errors && expected_errors[0];
if (error && expected) {
assert.equal(error.code, expected.code);
assert.equal(error.message, expected.message);
assert.deepEqual({ line: error.start?.line, column: error.start?.column }, expected.start);
assert.deepEqual({ line: error.end?.line, column: error.end?.column }, expected.end);
assert.deepEqual(
{
code: error.code,
message: error.message,
start: { line: error.start?.line, column: error.start?.column },
end: { line: error.end?.line, column: error.end?.column }
},
{
code: expected.code,
message: expected.message,
start: expected.start,
end: expected.end
}
);
} else if (expected) {
throw new Error(`Expected an error: ${expected.message}`);
} else if (error) {

Loading…
Cancel
Save