From 14eb6446b43f5f8736de46a2205889f31091321e Mon Sep 17 00:00:00 2001 From: tommywalkie Date: Mon, 14 Sep 2020 12:25:30 +0200 Subject: [PATCH 1/2] Add Svelte TypeScript preprocessing example --- examples/svelte-preprocess/package.json | 15 +++++ examples/svelte-preprocess/src/Count.svelte | 16 +++++ examples/svelte-preprocess/svelte.config.js | 9 +++ examples/svelte-preprocess/tests/Count.js | 67 +++++++++++++++++++ examples/svelte-preprocess/tests/setup/env.js | 47 +++++++++++++ .../tests/setup/preprocess.js | 9 +++ .../svelte-preprocess/tests/setup/register.js | 51 ++++++++++++++ .../tests/setup/svelteconfig.js | 28 ++++++++ 8 files changed, 242 insertions(+) create mode 100644 examples/svelte-preprocess/package.json create mode 100644 examples/svelte-preprocess/src/Count.svelte create mode 100644 examples/svelte-preprocess/svelte.config.js create mode 100644 examples/svelte-preprocess/tests/Count.js create mode 100644 examples/svelte-preprocess/tests/setup/env.js create mode 100644 examples/svelte-preprocess/tests/setup/preprocess.js create mode 100644 examples/svelte-preprocess/tests/setup/register.js create mode 100644 examples/svelte-preprocess/tests/setup/svelteconfig.js 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/preprocess.js b/examples/svelte-preprocess/tests/setup/preprocess.js new file mode 100644 index 0000000..a6dd40d --- /dev/null +++ b/examples/svelte-preprocess/tests/setup/preprocess.js @@ -0,0 +1,9 @@ +const svelte = require('svelte/compiler') +const { cosmiconfigSync } = require('cosmiconfig') + +const { source, filename, svelteConfig } = process.env +const config = cosmiconfigSync().load(svelteConfig).config + +svelte + .preprocess(source, config.preprocess || {}, { filename }) + .then(r => { process.stdout.write(r.code) }) \ No newline at end of file diff --git a/examples/svelte-preprocess/tests/setup/register.js b/examples/svelte-preprocess/tests/setup/register.js new file mode 100644 index 0000000..5dc0c56 --- /dev/null +++ b/examples/svelte-preprocess/tests/setup/register.js @@ -0,0 +1,51 @@ +const { parse } = require('path'); +const { compile } = require('svelte/compiler'); +const { execSync } = require('child_process'); +const { getSvelteConfig } = require('./svelteconfig.js'); + +const useTransformer = (options = {}) => (source, filename) => { + const { debug, preprocess, rootMode } = options; + let processed = source; + if (preprocess) { + const svelteConfig = getSvelteConfig(rootMode, filename); + const preprocessor = require.resolve('./preprocess.js'); + processed = execSync(`node --unhandled-rejections=strict --abort-on-uncaught-exception "${preprocessor}"`, { + env: { PATH: process.env.PATH, source, filename, svelteConfig } + }).toString(); + if (debug) console.log(processed); + return processed; + } + 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 +} From 1eabf2ae347d4699acab20cc755afbbab1113442 Mon Sep 17 00:00:00 2001 From: Andreas Ehrencrona Date: Fri, 11 Dec 2020 11:22:33 +0200 Subject: [PATCH 2/2] Adapted to use sync preprocess --- .../tests/setup/preprocess.js | 9 ----- .../svelte-preprocess/tests/setup/register.js | 34 ++++++++----------- 2 files changed, 15 insertions(+), 28 deletions(-) delete mode 100644 examples/svelte-preprocess/tests/setup/preprocess.js diff --git a/examples/svelte-preprocess/tests/setup/preprocess.js b/examples/svelte-preprocess/tests/setup/preprocess.js deleted file mode 100644 index a6dd40d..0000000 --- a/examples/svelte-preprocess/tests/setup/preprocess.js +++ /dev/null @@ -1,9 +0,0 @@ -const svelte = require('svelte/compiler') -const { cosmiconfigSync } = require('cosmiconfig') - -const { source, filename, svelteConfig } = process.env -const config = cosmiconfigSync().load(svelteConfig).config - -svelte - .preprocess(source, config.preprocess || {}, { filename }) - .then(r => { process.stdout.write(r.code) }) \ No newline at end of file diff --git a/examples/svelte-preprocess/tests/setup/register.js b/examples/svelte-preprocess/tests/setup/register.js index 5dc0c56..0c724c5 100644 --- a/examples/svelte-preprocess/tests/setup/register.js +++ b/examples/svelte-preprocess/tests/setup/register.js @@ -1,29 +1,25 @@ const { parse } = require('path'); -const { compile } = require('svelte/compiler'); -const { execSync } = require('child_process'); +const { compile, preprocess_sync } = require('svelte/compiler'); const { getSvelteConfig } = require('./svelteconfig.js'); +const { cosmiconfigSync } = require('cosmiconfig') const useTransformer = (options = {}) => (source, filename) => { - const { debug, preprocess, rootMode } = options; - let processed = source; - if (preprocess) { - const svelteConfig = getSvelteConfig(rootMode, filename); - const preprocessor = require.resolve('./preprocess.js'); - processed = execSync(`node --unhandled-rejections=strict --abort-on-uncaught-exception "${preprocessor}"`, { - env: { PATH: process.env.PATH, source, filename, svelteConfig } - }).toString(); - if (debug) console.log(processed); - return processed; - } - else { - return source; - } + 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 { name } = parse(filename); + + const preprocessed = useTransformer({ preprocess: true })(source, filename); const {js, warnings} = compile(preprocessed, { name: name[0].toUpperCase() + name.slice(1),