Skip to content

Commit

Permalink
feat: support compiling svelte.js/ts files in Svelte 5
Browse files Browse the repository at this point in the history
  • Loading branch information
dummdidumm committed Mar 11, 2024
1 parent ed67842 commit fe52a2d
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 51 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# rollup-plugin-svelte changelog

## 7.2.0

- Support compiling `svelte.js/ts` files in Svelte 5

## 7.1.6

- Adjust inferred `css` option for Svelte 4
Expand Down
28 changes: 23 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const path = require('path');
const fs = require('fs');
const { resolve } = require('resolve.exports');
const { createFilter } = require('@rollup/pluginutils');
const { compile, preprocess, VERSION } = require('svelte/compiler');
const svelte = require('svelte/compiler');

const PREFIX = '[rollup-plugin-svelte]';

Expand All @@ -26,7 +26,7 @@ module.exports = function (options = {}) {
const extensions = rest.extensions || ['.svelte'];
const filter = createFilter(rest.include, rest.exclude);

if (VERSION[0] === '3') {
if (svelte.VERSION[0] === '3') {
compilerOptions.format = 'esm';
}

Expand All @@ -42,7 +42,7 @@ module.exports = function (options = {}) {
const { onwarn, emitCss = true } = rest;

if (emitCss) {
const [majorVer] = VERSION.split('.');
const [majorVer] = svelte.VERSION.split('.');
const cssOptionValue = majorVer > 3 ? 'external' : false;
if (compilerOptions.css) {
console.warn(
Expand Down Expand Up @@ -129,6 +129,24 @@ module.exports = function (options = {}) {
async transform(code, id) {
if (!filter(id)) return null;

if (
Number(svelte.VERSION.split('.')[0]) >= 5 &&
(id.endsWith('.svelte.js') || id.endsWith('.svelte.ts'))
) {
const compiled = svelte.compileModule(code, {
filename: id,
dev: compilerOptions.dev,
generate: compilerOptions.generate,
});

(compiled.warnings || []).forEach((warning) => {
if (onwarn) onwarn(warning, this.warn);
else this.warn(warning);
});

return compiled.js;
}

const extension = path.extname(id);
if (!~extensions.indexOf(extension)) return null;

Expand All @@ -137,13 +155,13 @@ module.exports = function (options = {}) {
const svelte_options = { ...compilerOptions, filename };

if (rest.preprocess) {
const processed = await preprocess(code, rest.preprocess, { filename });
const processed = await svelte.preprocess(code, rest.preprocess, { filename });
if (processed.dependencies) dependencies.push(...processed.dependencies);
if (processed.map) svelte_options.sourcemap = processed.map;
code = processed.code;
}

const compiled = compile(code, svelte_options);
const compiled = svelte.compile(code, svelte_options);

(compiled.warnings || []).forEach((warning) => {
if (!emitCss && warning.code === 'css-unused-selector') return;
Expand Down
1 change: 1 addition & 0 deletions test/filename-test2/src/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { counter } from './runes.svelte.js';
1 change: 1 addition & 0 deletions test/filename-test2/src/runes.svelte.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const counter = $state({ value: 0 });
140 changes: 94 additions & 46 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const sander = require('sander');
const plugin = require('..');

const context = {
resolve: () => 'resolved'
resolve: () => 'resolved',
};

test('resolves using pkg.svelte', async () => {
Expand All @@ -22,15 +22,14 @@ test('resolves using pkg.svelte', async () => {

test('ignores built-in modules', async () => {
const p = plugin();
assert.is(
await p.resolveId.call(context, 'path', path.resolve('test/foo/main.js')), undefined
);
assert.is(await p.resolveId.call(context, 'path', path.resolve('test/foo/main.js')), undefined);
});

test('ignores esm modules that do not export package.json', async () => {
const p = plugin();
assert.is(
await p.resolveId.call(context, 'esm-no-pkg-export', path.resolve('test/foo/main.js')), undefined
await p.resolveId.call(context, 'esm-no-pkg-export', path.resolve('test/foo/main.js')),
undefined
);
});

Expand All @@ -45,7 +44,8 @@ test('resolves esm module that exports package.json', async () => {
test('ignores virtual modules', async () => {
const p = plugin();
assert.is(
await p.resolveId.call(context, 'path', path.resolve('\0some-plugin-generated-module')), undefined
await p.resolveId.call(context, 'path', path.resolve('\0some-plugin-generated-module')),
undefined
);
});

Expand All @@ -72,8 +72,8 @@ test('respects `sourcemapExcludeSources` Rollup option', async () => {

const bundle = await rollup({
input: 'test/sourcemap-test/src/main.js',
plugins: [ plugin({ emitCss: false }) ],
external: ['svelte/internal']
plugins: [plugin({ emitCss: false })],
external: ['svelte/internal'],
});

const { output } = await bundle.generate({
Expand All @@ -98,43 +98,48 @@ test('respects `sourcemapExcludeSources` Rollup option', async () => {

test('squelches "unused CSS" warnings if `emitCss: false`', () => {
const p = plugin({
emitCss: false
emitCss: false,
});

p.transform.call({
warn: warning => {
throw new Error(warning.message);
}
}, `
p.transform.call(
{
warn: (warning) => {
throw new Error(warning.message);
},
},
`
<div></div>
<style>
.unused {
color: red;
}
</style>
`, 'test.svelte');
`,
'test.svelte'
);
});

test('preprocesses components', async () => {
const p = plugin({
preprocess: {
markup: ({ content, filename }) => {
return {
code: content
.replace('__REPLACEME__', 'replaced')
.replace('__FILENAME__', filename),
code: content.replace('__REPLACEME__', 'replaced').replace('__FILENAME__', filename),
dependencies: ['foo'],
};
},
style: () => null,
}
},
});

const { code, dependencies } = await p.transform(`
const { code, dependencies } = await p.transform(
`
<h1>Hello __REPLACEME__!</h1>
<h2>file: __FILENAME__</h2>
<style>h1 { color: red; }</style>
`, 'test.svelte');
`,
'test.svelte'
);

assert.is(code.indexOf('__REPLACEME__'), -1, 'content not modified');
assert.is.not(code.indexOf('file: test.svelte'), -1, 'filename not replaced');
Expand All @@ -144,13 +149,16 @@ test('preprocesses components', async () => {
test('emits a CSS file', async () => {
const p = plugin();

const transformed = await p.transform(`<h1>Hello!</h1>
const transformed = await p.transform(
`<h1>Hello!</h1>
<style>
h1 {
color: red;
}
</style>`, `path/to/Input.svelte`);
</style>`,
`path/to/Input.svelte`
);

assert.ok(transformed.code.indexOf(`import "path/to/Input.css";`) !== -1);

Expand All @@ -160,7 +168,7 @@ test('emits a CSS file', async () => {

const loc = smc.originalPositionFor({
line: 1,
column: 0
column: 0,
});

assert.is(loc.source, 'Input.svelte');
Expand All @@ -171,13 +179,16 @@ test('emits a CSS file', async () => {
test('properly escapes CSS paths', async () => {
const p = plugin();

const transformed = await p.transform(`<h1>Hello!</h1>
const transformed = await p.transform(
`<h1>Hello!</h1>
<style>
h1 {
color: red;
}
</style>`, `path\\t'o\\Input.svelte`);
</style>`,
`path\\t'o\\Input.svelte`
);

assert.ok(transformed.code.indexOf(`import "path\\\\t'o\\\\Input.css";`) !== -1);

Expand All @@ -187,7 +198,7 @@ test('properly escapes CSS paths', async () => {

const loc = smc.originalPositionFor({
line: 1,
column: 0
column: 0,
});

assert.is(loc.source, 'Input.svelte');
Expand All @@ -206,20 +217,30 @@ test('intercepts warnings', async () => {
if (warning.code === 'a11y-hidden') {
handler(warning);
}
}
},
});

await p.transform.call({
warn: warning => {
handled.push(warning);
}
}, `
await p.transform.call(
{
warn: (warning) => {
handled.push(warning);
},
},
`
<h1 aria-hidden>Hello world!</h1>
<marquee>wheee!!!</marquee>
`, 'test.svelte');
`,
'test.svelte'
);

assert.equal(warnings.map(w => w.code), ['a11y-hidden', 'a11y-distracting-elements']);
assert.equal(handled.map(w => w.code), ['a11y-hidden']);
assert.equal(
warnings.map((w) => w.code),
['a11y-hidden', 'a11y-distracting-elements']
);
assert.equal(
handled.map((w) => w.code),
['a11y-hidden']
);
});

test('handles filenames that happen to contain ".svelte"', async () => {
Expand All @@ -233,12 +254,12 @@ test('handles filenames that happen to contain ".svelte"', async () => {
{
async resolveId(id) {
if (/A\.svelte/.test(id)) {
await new Promise(f => setTimeout(f, 50));
await new Promise((f) => setTimeout(f, 50));
}
}
},
},
plugin({
emitCss: true
emitCss: true,
}),
{
transform(code, id) {
Expand All @@ -250,10 +271,10 @@ test('handles filenames that happen to contain ".svelte"', async () => {
});
return '';
}
}
}
},
},
],
external: ['svelte/internal']
external: ['svelte/internal'],
});

await bundle.write({
Expand All @@ -274,6 +295,33 @@ test('handles filenames that happen to contain ".svelte"', async () => {
);
});

// Needs Svelte 5
test.skip('handles ".svelte.ts/js" files', async () => {
sander.rimrafSync('test/filename-test2/dist');
sander.mkdirSync('test/filename-test2/dist');

try {
const bundle = await rollup({
input: 'test/filename-test2/src/main.js',
plugins: [plugin({})],
external: ['svelte/internal'],
});

await bundle.write({
format: 'iife',
file: 'test/filename-test2/dist/bundle.js',
globals: { 'svelte/internal': 'svelte' },
assetFileNames: '[name].[ext]',
sourcemap: true,
});
} catch (err) {
console.log(err);
throw err;
}

assert.not(fs.readFileSync('test/filename-test2/dist/bundle.js', 'utf8').includes('$state'));
});

test('ignores ".html" extension by default', async () => {
sander.rimrafSync('test/node_modules/widget/dist');
sander.mkdirSync('test/node_modules/widget/dist');
Expand All @@ -282,7 +330,7 @@ test('ignores ".html" extension by default', async () => {
const bundle = await rollup({
input: 'test/node_modules/widget/index.js',
external: ['svelte/internal'],
plugins: [plugin()]
plugins: [plugin()],
});

await bundle.write({
Expand Down Expand Up @@ -311,9 +359,9 @@ test('allows ".html" extension if configured', async () => {
external: ['svelte/internal'],
plugins: [
plugin({
extensions: ['.html']
})
]
extensions: ['.html'],
}),
],
});

await bundle.write({
Expand Down

0 comments on commit fe52a2d

Please sign in to comment.