@sebastianwessel/quickjs
Version:
A typescript package to execute JavaScript and TypeScript code in a WebAssembly QuickJS sandbox
113 lines (112 loc) • 4.32 kB
JavaScript
import { join } from 'node:path';
/**
* Add support for handling typescript files and code.
* Requires the optional dependency 'typescript'.
*/
export const getTypescriptSupport = async (enabled = false, typescriptImportFile, options) => {
if (!enabled) {
return {
transpileFile: (value, _compilerOptions, _fileName, _diagnostics, _moduleName) => value,
transpileNestedDirectoryJSON: (mountFsJson, _option) => mountFsJson,
transpileVirtualFs: (fs, _options) => {
return fs;
},
};
}
let ts;
try {
ts = await import(typescriptImportFile ?? 'typescript');
}
catch (error) {
throw new Error('Package "typescript" is missing');
}
const compilerOptions = {
module: 99, // ESNext
target: 99, // ES2023
//lib: ['ESNext'],
allowJs: true,
// moduleResolution: 100,
skipLibCheck: true,
esModuleInterop: true,
strict: false,
allowSyntheticDefaultImports: true,
...options,
};
/**
* Transpile a single File
*
* @param params source typescript code
* @returns javascript code
*/
const transpileFile = (input, cpOptions = compilerOptions, fileName, diagnostics, moduleName) => ts.transpile(input, cpOptions, fileName, diagnostics, moduleName);
/**
* Iterates through the given JSON - NestedDirectoryJSON for defining the virtual file system.
* Replace every typescript file with the transpiled javascript version and renames the file to *.js
*
* @param mountFsJson
* @param fileExtensions
* @returns
*/
const transpileNestedDirectoryJSON = (mountFsJson, option) => {
const opt = {
fileExtensions: ['ts'],
...option,
};
const transformJson = (obj, transformer) => {
if (typeof obj === 'object' && obj !== null) {
const newObj = Array.isArray(obj) ? [] : {};
for (const key in obj) {
if (obj[key]) {
const [newKey, newValue] = transformer(key, obj[key]);
newObj[newKey] = transformJson(newValue, transformer);
}
}
return newObj;
}
return obj;
};
const transpileTypescript = (key, value) => {
if (!opt.fileExtensions.includes(key)) {
return [key, value];
}
const newFileName = key.replace('.ts', '.js');
const tsSource = typeof value === 'string' ? value : value.toString();
const jsSource = transpileFile(tsSource, compilerOptions);
return [newFileName, jsSource];
};
return transformJson(mountFsJson, transpileTypescript);
};
const transpileVirtualFs = (fs, options) => {
const opt = {
startPath: '/src',
...options,
};
const transformFileSystem = (startPath) => {
if (!fs.existsSync(startPath)) {
throw new Error(`Directory not found: ${startPath}`);
}
const files = fs.readdirSync(startPath);
for (const file of files) {
const filePath = join(startPath, file);
const stat = fs.lstatSync(filePath);
if (stat.isDirectory()) {
// ignore node_modules
if (file !== 'node_modules') {
transformFileSystem(filePath);
}
}
else if (stat.isFile()) {
const newFilePath = join(startPath, file.replace('.ts', '.js'));
const content = fs.readFileSync(filePath, 'utf8');
const tsSource = typeof content === 'string' ? content : content.toString();
const jsSource = transpileFile(tsSource, compilerOptions, newFilePath);
fs.renameSync(filePath, newFilePath);
fs.writeFileSync(newFilePath, jsSource);
}
}
};
transformFileSystem(opt.startPath);
return fs;
};
return { transpileFile, transpileNestedDirectoryJSON, transpileVirtualFs };
};