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

Synchronous preprocessing for Svelte TypeScript tests #85

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions examples/svelte-preprocess/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"private": true,
"scripts": {
"test": "uvu tests -r esm -r tests/setup/register -i setup"
},
"devDependencies": {
"cosmiconfig": "^7.0.0",
"esm": "3.2.25",
"jsdom": "16.3.0",
"svelte": "3.24.0",
"svelte-preprocess": "^4.2.1",
"typescript": "^3.9.7",
"uvu": "^0.2.0"
}
}
16 changes: 16 additions & 0 deletions examples/svelte-preprocess/src/Count.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script>
type CountResult = number
export let count: CountResult = 5;

function increment(): void {
count++;
}

function decrement(): void {
count--;
}
</script>

<button id="decr" on:click={decrement}>--</button>
<span>{count}</span>
<button id="incr" on:click={increment}>++</button>
9 changes: 9 additions & 0 deletions examples/svelte-preprocess/svelte.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const sveltePreprocess = require("svelte-preprocess");

const defaults = {
script: "typescript",
};

module.exports = {
preprocess: sveltePreprocess({ defaults })
}
67 changes: 67 additions & 0 deletions examples/svelte-preprocess/tests/Count.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { test } from 'uvu';
import * as assert from 'uvu/assert';
import * as ENV from './setup/env';

// Relies on `setup/register`
import Count from '../src/Count.svelte';

test.before(ENV.setup);
test.before.each(ENV.reset);

test('should render with "5" by default', () => {
const { container } = ENV.render(Count);

assert.snapshot(
container.innerHTML,
`<button id="decr">--</button> <span>5</span> <button id="incr">++</button>`
);
});

test('should accept custom `count` prop', () => {
const { container } = ENV.render(Count, { count: 99 });

assert.snapshot(
container.innerHTML,
`<button id="decr">--</button> <span>99</span> <button id="incr">++</button>`
);
});

test('should increment count after `button#incr` click', async () => {
const { container } = ENV.render(Count);

assert.snapshot(
container.innerHTML,
`<button id="decr">--</button> <span>5</span> <button id="incr">++</button>`
);

await ENV.fire(
container.querySelector('#incr'),
'click'
);

assert.snapshot(
container.innerHTML,
`<button id="decr">--</button> <span>6</span> <button id="incr">++</button>`
);
});

test('should decrement count after `button#decr` click', async () => {
const { container } = ENV.render(Count);

assert.snapshot(
container.innerHTML,
`<button id="decr">--</button> <span>5</span> <button id="incr">++</button>`
);

await ENV.fire(
container.querySelector('#decr'),
'click'
);

assert.snapshot(
container.innerHTML,
`<button id="decr">--</button> <span>4</span> <button id="incr">++</button>`
);
});

test.run();
47 changes: 47 additions & 0 deletions examples/svelte-preprocess/tests/setup/env.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { JSDOM } from 'jsdom';
import { tick } from 'svelte';

const { window } = new JSDOM('');

export function setup() {
// @ts-ignore
global.window = window;
global.document = window.document;
global.navigator = window.navigator;
global.getComputedStyle = window.getComputedStyle;
global.requestAnimationFrame = null;
}

export function reset() {
window.document.title = '';
window.document.head.innerHTML = '';
window.document.body.innerHTML = '';
}

/**
* @typedef RenderOutput
* @property container {HTMLElement}
* @property component {import('svelte').SvelteComponent}
*/

/**
* @return {RenderOutput}
*/
export function render(Tag, props = {}) {
Tag = Tag.default || Tag;
const container = window.document.body;
const component = new Tag({ props, target: container });
return { container, component };
}

/**
* @param {HTMLElement} elem
* @param {String} event
* @param {any} [details]
* @returns Promise<void>
*/
export function fire(elem, event, details) {
let evt = new window.Event(event, details);
elem.dispatchEvent(evt);
return tick();
}
47 changes: 47 additions & 0 deletions examples/svelte-preprocess/tests/setup/register.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const { parse } = require('path');
const { compile, preprocess_sync } = require('svelte/compiler');
const { getSvelteConfig } = require('./svelteconfig.js');
const { cosmiconfigSync } = require('cosmiconfig')

const useTransformer = (options = {}) => (source, filename) => {
const { preprocess, rootMode } = options;
if (preprocess) {
const svelteConfig = getSvelteConfig(rootMode, filename);
const config = cosmiconfigSync().load(svelteConfig).config
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably unnecessary to load the config for every file to be processed.


return preprocess_sync(source, config.preprocess || {}, { filename }).code
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line is pretty much the only change compared to #49

}
else {
return source;
}
};

function transform(hook, source, filename) {
const { name } = parse(filename);

const preprocessed = useTransformer({ preprocess: true })(source, filename);

const {js, warnings} = compile(preprocessed, {
name: name[0].toUpperCase() + name.slice(1),
format: 'cjs',
filename
});

warnings.forEach(warning => {
console.warn(`\nSvelte Warning in ${warning.filename}:`);
console.warn(warning.message);
console.warn(warning.frame);
});

return hook(js.code, filename);
}

const loadJS = require.extensions['.js'];

// Runtime DOM hook for require("*.svelte") files
// Note: for SSR/Node.js hook, use `svelte/register`
require.extensions['.svelte'] = function (mod, filename) {
const orig = mod._compile.bind(mod);
mod._compile = code => transform(orig, code, filename);
loadJS(mod, filename);
}
28 changes: 28 additions & 0 deletions examples/svelte-preprocess/tests/setup/svelteconfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const fs = require('fs')
const path = require('path')

const configFilename = 'svelte.config.js'

exports.getSvelteConfig = (rootMode, filename) => {
const configDir = rootMode === 'upward'
? getConfigDir(path.dirname(filename))
: process.cwd()
const configFile = path.resolve(configDir, configFilename)

if (!fs.existsSync(configFile)) {
throw Error(`Could not find ${configFilename}`)
}

return configFile
}

const getConfigDir = (searchDir) => {
if (fs.existsSync(path.join(searchDir, configFilename))) {
return searchDir
}

const parentDir = path.resolve(searchDir, '..')
return parentDir !== searchDir
? getConfigDir(parentDir)
: searchDir // Stop walking at filesystem root
}