diff --git a/examples/svelte-preprocess/package.json b/examples/svelte-preprocess/package.json new file mode 100644 index 0000000..8e5cb41 --- /dev/null +++ b/examples/svelte-preprocess/package.json @@ -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" + } +} diff --git a/examples/svelte-preprocess/src/Count.svelte b/examples/svelte-preprocess/src/Count.svelte new file mode 100644 index 0000000..4dad687 --- /dev/null +++ b/examples/svelte-preprocess/src/Count.svelte @@ -0,0 +1,16 @@ + + + +{count} + diff --git a/examples/svelte-preprocess/svelte.config.js b/examples/svelte-preprocess/svelte.config.js new file mode 100644 index 0000000..1958389 --- /dev/null +++ b/examples/svelte-preprocess/svelte.config.js @@ -0,0 +1,9 @@ +const sveltePreprocess = require("svelte-preprocess"); + +const defaults = { + script: "typescript", +}; + +module.exports = { + preprocess: sveltePreprocess({ defaults }) +} \ No newline at end of file diff --git a/examples/svelte-preprocess/tests/Count.js b/examples/svelte-preprocess/tests/Count.js new file mode 100644 index 0000000..c99c558 --- /dev/null +++ b/examples/svelte-preprocess/tests/Count.js @@ -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, + ` 5 ` + ); +}); + +test('should accept custom `count` prop', () => { + const { container } = ENV.render(Count, { count: 99 }); + + assert.snapshot( + container.innerHTML, + ` 99 ` + ); +}); + +test('should increment count after `button#incr` click', async () => { + const { container } = ENV.render(Count); + + assert.snapshot( + container.innerHTML, + ` 5 ` + ); + + await ENV.fire( + container.querySelector('#incr'), + 'click' + ); + + assert.snapshot( + container.innerHTML, + ` 6 ` + ); +}); + +test('should decrement count after `button#decr` click', async () => { + const { container } = ENV.render(Count); + + assert.snapshot( + container.innerHTML, + ` 5 ` + ); + + await ENV.fire( + container.querySelector('#decr'), + 'click' + ); + + assert.snapshot( + container.innerHTML, + ` 4 ` + ); +}); + +test.run(); diff --git a/examples/svelte-preprocess/tests/setup/env.js b/examples/svelte-preprocess/tests/setup/env.js new file mode 100644 index 0000000..00fac9c --- /dev/null +++ b/examples/svelte-preprocess/tests/setup/env.js @@ -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 + */ +export function fire(elem, event, details) { + let evt = new window.Event(event, details); + elem.dispatchEvent(evt); + return tick(); +} diff --git a/examples/svelte-preprocess/tests/setup/register.js b/examples/svelte-preprocess/tests/setup/register.js new file mode 100644 index 0000000..0c724c5 --- /dev/null +++ b/examples/svelte-preprocess/tests/setup/register.js @@ -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 + + return preprocess_sync(source, config.preprocess || {}, { filename }).code + } + 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); +} diff --git a/examples/svelte-preprocess/tests/setup/svelteconfig.js b/examples/svelte-preprocess/tests/setup/svelteconfig.js new file mode 100644 index 0000000..5a231e9 --- /dev/null +++ b/examples/svelte-preprocess/tests/setup/svelteconfig.js @@ -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 +}