mirror of https://github.com/sveltejs/svelte
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.
189 lines
5.0 KiB
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: () => {}
|
|
};
|
|
});
|
|
}
|