diff --git a/.changeset/swift-seahorses-deliver.md b/.changeset/swift-seahorses-deliver.md new file mode 100644 index 0000000000..8d8189d910 --- /dev/null +++ b/.changeset/swift-seahorses-deliver.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +feat: throw descriptive error for using runes globals outside of Svelte-compiled files diff --git a/packages/svelte/scripts/check-treeshakeability.js b/packages/svelte/scripts/check-treeshakeability.js index 503501292c..fd55ce096e 100644 --- a/packages/svelte/scripts/check-treeshakeability.js +++ b/packages/svelte/scripts/check-treeshakeability.js @@ -2,6 +2,7 @@ import fs from 'node:fs'; import path from 'node:path'; import { rollup } from 'rollup'; import virtual from '@rollup/plugin-virtual'; +import { nodeResolve } from '@rollup/plugin-node-resolve'; const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8')); @@ -27,6 +28,9 @@ for (const key in pkg.exports) { plugins: [ virtual({ __entry__: `import ${JSON.stringify(resolved)}` + }), + nodeResolve({ + exportConditions: ['production', 'import', 'browser', 'default'] }) ], onwarn: (warning, handle) => { @@ -40,16 +44,17 @@ for (const key in pkg.exports) { throw new Error('errr what'); } - const code = output[0].code.replace(/import\s+([^'"]+from\s+)?(['"])[^'"]+\2\s*;?/, ''); - if (code.trim()) { + const code = output[0].code.trim(); + + if (code === '') { + // eslint-disable-next-line no-console + console.error(`✅ ${subpackage} (${type})`); + } else { // eslint-disable-next-line no-console console.error(code); // eslint-disable-next-line no-console console.error(`❌ ${subpackage} (${type})`); failed = true; - } else { - // eslint-disable-next-line no-console - console.error(`✅ ${subpackage} (${type})`); } } } diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 462372f83f..08c5f78f2e 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -1875,3 +1875,22 @@ export function unwrap(value) { // @ts-ignore return value; } + +if (DEV) { + /** @param {string} rune */ + function throw_rune_error(rune) { + if (!(rune in globalThis)) { + // @ts-ignore + globalThis[rune] = () => { + // TODO if people start adjusting the "this can contain runes" config through v-p-s more, adjust this message + throw new Error(`${rune} is only available inside .svelte and .svelte.js/ts files`); + }; + } + } + + throw_rune_error('$state'); + throw_rune_error('$effect'); + throw_rune_error('$derived'); + throw_rune_error('$inspect'); + throw_rune_error('$props'); +}