From d6b02ed0095a93c9a7ff64f62966995c00ff2273 Mon Sep 17 00:00:00 2001 From: gtmnayan <50981692+gtm-nayan@users.noreply.github.com> Date: Tue, 23 May 2023 21:37:56 +0545 Subject: [PATCH] chore(test): use esm output for Svelte files (#8614) --- test/css/css.test.js | 17 +--- test/helpers.js | 106 +++++++++++++++++------ test/hydration/hydration.test.js | 1 - test/runtime/runtime.shared.js | 1 - test/runtime/runtime_base.test.js | 2 +- test/server-side-rendering/ssr-1.test.js | 3 +- test/server-side-rendering/ssr-2.test.js | 3 +- 7 files changed, 88 insertions(+), 45 deletions(-) diff --git a/test/css/css.test.js b/test/css/css.test.js index eed4862e60..c6524a5137 100644 --- a/test/css/css.test.js +++ b/test/css/css.test.js @@ -34,15 +34,9 @@ describe('css', () => { const expected_warnings = (config.warnings || []).map(normalize_warning); - const dom = svelte.compile( - input, - Object.assign({}, config.compileOptions || {}, { format: 'cjs' }) - ); + const dom = svelte.compile(input, Object.assign({}, config.compileOptions || {})); - const ssr = svelte.compile( - input, - Object.assign({}, config.compileOptions || {}, { format: 'cjs', generate: 'ssr' }) - ); + const ssr = svelte.compile(input, Object.assign({}, config.compileOptions || {})); assert.equal(dom.css.code, ssr.css.code); @@ -75,7 +69,7 @@ describe('css', () => { // we do this here, rather than in the expected.html !== null // block, to verify that valid code was generated - const load = create_loader({ ...(config.compileOptions || {}), format: 'cjs' }, cwd); + const load = create_loader({ ...(config.compileOptions || {}) }, cwd); try { ClientComponent = (await load('input.svelte')).default; } catch (err) { @@ -83,10 +77,7 @@ describe('css', () => { throw err; } - const load_ssr = create_loader( - { ...(config.compileOptions || {}), generate: 'ssr', format: 'cjs' }, - cwd - ); + const load_ssr = create_loader({ ...(config.compileOptions || {}), generate: 'ssr' }, cwd); try { ServerComponent = (await load_ssr('input.svelte')).default; } catch (err) { diff --git a/test/helpers.js b/test/helpers.js index 5dfa80e542..8e6741dc21 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -103,6 +103,8 @@ export function show_output(cwd, options = {}) { const svelte_path = fileURLToPath(new URL('..', import.meta.url)).replace(/\\/g, '/'); +const AsyncFunction = /** @type {typeof Function} */ (async function () {}.constructor); + export function create_loader(compileOptions, cwd) { const cache = new Map(); @@ -110,46 +112,100 @@ export function create_loader(compileOptions, cwd) { if (cache.has(file)) return cache.get(file); if (file.endsWith('.svelte')) { + const options = { + ...compileOptions, + filename: file + }; + const compiled = compile( // Windows/Linux newline conversion fs.readFileSync(file, 'utf-8').replace(/\r\n/g, '\n'), - { - ...compileOptions, - filename: file - } + options ); - const imports = new Map(); + const __import = (id) => { + let resolved = id; - for (const match of compiled.js.code.matchAll(/require\("(.+?)"\)/g)) { - const source = match[1]; - let resolved = source; - - if (source.startsWith('.')) { - resolved = path.resolve(path.dirname(file), source); + if (id.startsWith('.')) { + resolved = path.resolve(path.dirname(file), id); } - if (source === 'svelte') { + if (id === 'svelte') { resolved = `${svelte_path}src/runtime/index.js`; } - if (source.startsWith('svelte/')) { - resolved = `${svelte_path}src/runtime/${source.slice(7)}/index.js`; + if (id.startsWith('svelte/')) { + resolved = `${svelte_path}src/runtime/${id.slice(7)}/index.js`; } - imports.set(source, await load(resolved)); - } - - function require(id) { - return imports.get(id); + return load(resolved); + }; + + const exports = []; + + // We can't use Node's or Vitest's loaders cause we compile with different options. + // We need to rewrite the imports into function calls that we can intercept to transform + // any imported Svelte components as well. A few edge cases aren't handled but also + // currently unused in the tests, for example `export * from`and live bindings. + let transformed = compiled.js.code + .replace( + /^import \* as (\w+) from ['"]([^'"]+)['"];?/gm, + 'const $1 = await __import("$2");' + ) + .replace( + /^import (\w+) from ['"]([^'"]+)['"];?/gm, + 'const {default: $1} = await __import("$2");' + ) + .replace( + /^import (\w+, )?{([^}]+)} from ['"](.+)['"];?/gm, + (_, default_, names, source) => { + const d = default_ ? `default: ${default_}` : ''; + return `const { ${d} ${names.replaceAll( + ' as ', + ': ' + )} } = await __import("${source}");`; + } + ) + .replace(/^export default /gm, '__exports.default = ') + .replace( + /^export (const|let|var|class|function|async\s+function) (\w+)/gm, + (_, type, name) => { + exports.push(name); + return `${type} ${name}`; + } + ) + .replace(/^export \{([^}]+)\}(?: from ['"]([^'"]+)['"];?)?/gm, (_, names, source) => { + const entries = names.split(',').map((name) => { + const match = name.trim().match(/^(\w+)( as (\w+))?$/); + const i = match[1]; + const o = match[3] || i; + + return [o, i]; + }); + return source + ? `{ const __mod = await __import("${source}"); ${entries + .map(([o, i]) => `__exports.${o} = __mod.${i};`) + .join('\n')}}` + : `{ ${entries.map(([o, i]) => `__exports.${o} = ${i};`).join('\n')} }`; + }); + + exports.forEach((name) => { + transformed += `\n__exports.${name} = ${name};`; + }); + + const __exports = { + [Symbol.toStringTag]: 'Module' + }; + try { + const fn = new AsyncFunction('__import', '__exports', transformed); + await fn(__import, __exports); + } catch (err) { + console.error({ transformed }); // eslint-disable-line no-console + throw err; } - const fn = new Function('require', 'exports', 'module', compiled.js.code); - const module = { exports: {} }; - fn(require, module.exports, module); - - cache.set(file, module.exports); - return module.exports; + cache.set(file, __exports); + return __exports; } else { return import(file); } diff --git a/test/hydration/hydration.test.js b/test/hydration/hydration.test.js index 4ba43d1a7a..8edfb47422 100644 --- a/test/hydration/hydration.test.js +++ b/test/hydration/hydration.test.js @@ -21,7 +21,6 @@ describe('hydration', async () => { const compileOptions = Object.assign({}, config.compileOptions, { accessors: 'accessors' in config ? config.accessors : true, - format: 'cjs', hydratable: true }); diff --git a/test/runtime/runtime.shared.js b/test/runtime/runtime.shared.js index 95a648771b..0d317fb360 100644 --- a/test/runtime/runtime.shared.js +++ b/test/runtime/runtime.shared.js @@ -56,7 +56,6 @@ async function run_test(dir) { const cwd = path.resolve(`${__dirname}/samples/${dir}`); const compileOptions = Object.assign({}, config.compileOptions || {}, { - format: 'cjs', hydratable: hydrate, immutable: config.immutable, accessors: 'accessors' in config ? config.accessors : true diff --git a/test/runtime/runtime_base.test.js b/test/runtime/runtime_base.test.js index 756c63c187..4f912eb444 100644 --- a/test/runtime/runtime_base.test.js +++ b/test/runtime/runtime_base.test.js @@ -3,7 +3,7 @@ import { create_loader } from '../helpers'; import { assert, it } from 'vitest'; -const load = create_loader({ generate: 'dom', dev: true, format: 'cjs' }, __dirname); +const load = create_loader({ generate: 'dom', dev: true }, __dirname); const { default: App } = await load('App.svelte'); it('fails if options.target is missing in dev mode', async () => { diff --git a/test/server-side-rendering/ssr-1.test.js b/test/server-side-rendering/ssr-1.test.js index 9eb19aae41..43732e8eb7 100644 --- a/test/server-side-rendering/ssr-1.test.js +++ b/test/server-side-rendering/ssr-1.test.js @@ -30,8 +30,7 @@ describe('ssr', async () => { const compileOptions = { ...config.compileOptions, - generate: 'ssr', - format: 'cjs' + generate: 'ssr' }; const load = create_loader(compileOptions, dir); diff --git a/test/server-side-rendering/ssr-2.test.js b/test/server-side-rendering/ssr-2.test.js index acf823d0b0..f24edfc2c7 100644 --- a/test/server-side-rendering/ssr-2.test.js +++ b/test/server-side-rendering/ssr-2.test.js @@ -33,8 +33,7 @@ function run_runtime_samples(suite) { it_fn(dir, async () => { const compileOptions = { ...config.compileOptions, - generate: 'ssr', - format: 'cjs' + generate: 'ssr' }; const load = create_loader(compileOptions, cwd);