You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte/packages/svelte/tests/helpers.js

189 lines
5.0 KiB

/** @import { CompileOptions } from '#compiler' */
import * as fs from 'node:fs';
import * as path from 'node:path';
import { globSync } from 'tinyglobby';
import { VERSION, compile, compileModule, preprocess } from 'svelte/compiler';
import { vi } from 'vitest';
/**
* @param {string} file
*/
export function try_load_json(file) {
try {
return JSON.parse(fs.readFileSync(file, 'utf-8'));
} catch (err) {
if (/** @type {any} */ (err).code !== 'ENOENT') throw err;
return null;
}
}
/**
* @param {string} file
*/
export function try_read_file(file) {
try {
return read_file(file);
} catch (err) {
if (/** @type {any} */ (err).code !== 'ENOENT') throw err;
return null;
}
}
/**
* @param {string} file
*/
export function read_file(file) {
return fs.readFileSync(file, 'utf-8').replace(/\r\n/g, '\n');
}
export function create_deferred() {
/** @param {any} [value] */
let resolve = (value) => {};
/** @param {any} [reason] */
let reject = (reason) => {};
const promise = new Promise((f, r) => {
resolve = f;
reject = r;
});
return { promise, resolve, reject };
}
/**
*
* @param {string} cwd
* @param {'client' | 'server'} generate
* @param {Partial<CompileOptions>} compileOptions
* @param {boolean} [output_map]
* @param {any} [preprocessor]
*/
export async function compile_directory(
cwd,
generate,
compileOptions = {},
output_map = false,
preprocessor
) {
const output_dir = `${cwd}/_output/${generate}`;
fs.rmSync(output_dir, { recursive: true, force: true });
for (let file of globSync('**', { cwd, onlyFiles: true })) {
if (file.startsWith('_')) continue;
let text = fs.readFileSync(`${cwd}/${file}`, 'utf-8').replace(/\r\n/g, '\n');
let opts = {
filename: path.join(cwd, file),
...compileOptions,
generate
};
if (file.endsWith('.js')) {
const out = `${output_dir}/${file}`;
if (file.endsWith('.svelte.js')) {
const compiled = compileModule(text, {
filename: opts.filename,
generate: opts.generate,
dev: opts.dev
});
write(out, compiled.js.code.replace(`v${VERSION}`, 'VERSION'));
} else {
// for non-runes tests, just re-export from the original source file — this
// allows the `_config.js` module to import shared state to use in tests
const source = path
.relative(path.dirname(out), path.resolve(cwd, file))
.replace(/\\/g, '/');
let result = `export * from '${source}';`;
if (text.includes('export default')) {
result += `\nexport { default } from '${source}';`;
}
write(out, result);
}
} else if (
file.endsWith('.svelte') &&
// Make it possible to compile separate versions for client and server to simulate
// cases where `{browser ? 'foo' : 'bar'}` is turning into `{'foo'}` on the server
// and `{bar}` on the client, assuming we have sophisticated enough treeshaking
// in the future to make this a thing.
(!file.endsWith('.server.svelte') || generate === 'server') &&
(!file.endsWith('.client.svelte') || generate === 'client')
) {
file = file.replace(/\.client\.svelte$/, '.svelte').replace(/\.server\.svelte$/, '.svelte');
if (preprocessor?.preprocess) {
const preprocessed = await preprocess(
text,
preprocessor.preprocess,
preprocessor.options || {
filename: opts.filename
}
);
text = preprocessed.code;
opts = { ...opts, sourcemap: preprocessed.map };
write(`${output_dir}/${file.slice(0, -7)}.preprocessed.svelte`, text);
if (output_map) {
write(
`${output_dir}/${file.slice(0, -7)}.preprocessed.svelte.map`,
JSON.stringify(preprocessed.map, null, '\t')
);
}
}
const compiled = compile(text, {
outputFilename: `${output_dir}/${file}${file.endsWith('.js') ? '' : '.js'}`,
cssOutputFilename: `${output_dir}/${file}.css`,
...opts
});
compiled.js.code = compiled.js.code.replace(`v${VERSION}`, 'VERSION');
write(`${output_dir}/${file}.js`, compiled.js.code);
if (output_map) {
write(`${output_dir}/${file}.js.map`, JSON.stringify(compiled.js.map, null, '\t'));
}
if (compiled.css) {
write(`${output_dir}/${file}.css`, compiled.css.code);
if (output_map) {
write(`${output_dir}/${file}.css.map`, JSON.stringify(compiled.css.map, null, '\t'));
}
}
if (compiled.warnings.length > 0) {
write(`${output_dir}/${file}.warnings.json`, JSON.stringify(compiled.warnings, null, '\t'));
}
}
}
}
export function should_update_expected() {
return process.env.SHOULD_UPDATE_EXPECTED === 'true';
}
/**
* @param {string} file
* @param {string} contents
*/
export function write(file, contents) {
try {
fs.mkdirSync(path.dirname(file), { recursive: true });
} catch {}
fs.writeFileSync(file, contents);
}
// Guard because not all test contexts load this with JSDOM
if (typeof window !== 'undefined') {
// @ts-expect-error JS DOM doesn't support it
Window.prototype.matchMedia = vi.fn((media) => {
return {
matches: false,
media,
addEventListener: () => {},
removeEventListener: () => {}
};
});
}