tstosc
Version:
A transpiler that convert TypeScript to SuperCollider's SCLang.
183 lines (180 loc) • 7.91 kB
JavaScript
import { version } from '../prelude.js';
import minimist from 'minimist';
import { seperateArgs, parseArgs } from './args.js';
import { showBriefHelp, showHelp } from './help.js';
import { error, askToProceedOrAbort, warn, success, printError, dim } from './console.js';
import path from 'path';
import fs from 'fs';
import { findPreprocessingNeededByCached, convertTSSourceFileToSC } from '../transpiler/generator/ts_to_sc_convert/file_conv.js';
const [cli_global_argv, cli_positional_argvs] = seperateArgs(process.argv.slice(2));
const global_arg = minimist(cli_global_argv, {
alias: {
"version": ["v"],
"help": ["h"],
"out-dir": ["d"],
"flatten": [],
"user-extension-dir": ["u"],
"project-name": ["p"],
"yes-to-all": ["y"]
},
string: [
"help",
"out-dir",
"user-extension-dir",
"project-name"
]
});
const positional_args = cli_positional_argvs.map((group) => minimist(group, {
alias: {
"out-dir": ["d"],
"out": ["o"]
}
})).map((o) => ({ ...o, "_": o["_"][0] ?? "" }));
async function main() {
const parsed_intention = parseArgs(global_arg, positional_args);
switch (parsed_intention.type) {
case "transpile":
{
const { yes_to_all } = parsed_intention.global;
const { user_extension_dir, project_name } = parsed_intention.global;
const tstosc_helper_file_path = path.resolve(user_extension_dir, "tstosc__store");
const project_extension_path = path.resolve(user_extension_dir, project_name);
if (tstosc_helper_file_path == project_extension_path) {
console.log(error(
`Error: User Extension Dir cannot be set to "${tstosc_helper_file_path}".
This is the directory for TStoSC to store its helper file, but not for user's class.
You may avoid this by setting another project name by global option "--project-name" ("-p").
Aborted: Not OK to over-write files in User Extension Dir.`
));
return;
} else {
writeTStoSCHelperFile(tstosc_helper_file_path);
}
{
if (!yes_to_all && fs.existsSync(project_extension_path) && !askToProceedOrAbort(warn(
`Warn: User Extension Dir "${project_extension_path}" already exists.
You can set a project name by global option "--project-name" ("-p") to avoid unintended file lost.
It is OK to clear-then-write to that dir ? (type "y" to proceed): `
))) {
console.log(error(`Aborted: Not OK to over-write files in User Extension Dir.`));
return;
}
console.log(`Hint: You can use "--yes-to-all" ("-y") option to answer all warnings with yes.`);
}
const { Analyser, Generator } = await import('../transpiler/exports.js');
const analyser = new Analyser(parsed_intention);
const generator = new Generator({ compiler_program: analyser.getCompilerProgram() });
if (fs.existsSync(project_extension_path)) {
fs.rmSync(project_extension_path, { recursive: true });
}
const compiler_program = analyser.getCompilerProgram();
const class_conversion_packs = compiler_program.getRootFileNames().reduce(
(result, file_name) => {
const source_file = compiler_program.getSourceFile(file_name);
const defs = findPreprocessingNeededByCached(source_file).class_definitions;
if (defs.length > 0) {
result.push({
file_name: source_file.fileName,
file: source_file,
class_count: defs.length
});
}
return result;
},
// Avoid to import `typescript` in the top of file, because that is slow.
[]
);
if (class_conversion_packs.length > 0) {
console.log(success(`Generated classes: in "${project_extension_path}".`));
const class_path = path.resolve(project_extension_path, "classes");
fs.mkdirSync(class_path, { recursive: true });
for (const p of class_conversion_packs) {
const to_write__file_base_name = path.relative(process.cwd(), p.file_name).replace(/\.ts$/, "").replace(/[^\w]/g, "_") + ".sc";
const to_write__file_path = path.resolve(class_path, to_write__file_base_name);
const class_conv = convertTSSourceFileToSC(p.file, generator.getContext(), { convert_file: false });
if (class_conv.isErr()) {
console.log(error("Error when generating classes: ").indent(1));
printError(class_conv.unwrapErr(), 1);
console.log(error(`Fail to generate class in "${to_write__file_base_name}", see error above.`.indent(1)));
} else {
fs.writeFileSync(to_write__file_path, class_conv.unwrapOk().class);
console.log(success(`${p.class_count} class in "${to_write__file_base_name}".`.indent(1)));
}
}
} else {
console.log(dim(success("No class generated.")));
}
console.log(success(`Generated files:`));
const generated_path = /* @__PURE__ */ new Set();
for (const f of parsed_intention.files) {
let output_path = path.join(f.output_dir, f.output_name);
if (generated_path.has(output_path)) {
output_path = path.join(
f.output_dir,
`${f.input_path.replace(/[^\w]/g, "_")}.scd`
);
}
const result = generator.generateFile({
name: f.input_path,
output_path,
convertTSSourceFileToSC_option: { convert_class: false }
});
if (result.isErr()) {
console.log(error("Error when generating files: ").indent(1));
printError(result.unwrapErr(), 1);
} else {
generated_path.add(output_path);
console.log(success(
`"${f.input_path}"
---> "${path.relative(process.cwd(), output_path)}" ("${output_path}")`
).indent(1));
}
}
}
break;
case "query_info": {
switch (parsed_intention.command) {
case "help":
return showHelp(parsed_intention.arg);
case "version":
return showVersion();
}
}
// break // Already breaked by `return`.
case "no_intention":
{
showNameAndVersion();
showBriefHelp();
}
break;
case "unknown_or_error": {
if (parsed_intention.hint != void 0) {
console.log(parsed_intention.hint);
}
return 1;
}
}
}
function showNameAndVersion() {
console.log(
"TStoSC: Transpile TypeScript to SuperCollider's SCLang. Version " + version + ".\n"
);
}
function showVersion() {
console.log(`v${version}`);
}
function writeTStoSCHelperFile(helper_file_path) {
if (fs.existsSync(helper_file_path)) {
fs.rmSync(helper_file_path, { recursive: true });
}
fs.mkdirSync(helper_file_path, { recursive: true });
const helper_class_folder_path = path.resolve(helper_file_path, "class");
fs.mkdirSync(helper_class_folder_path);
const helper_class_file_path = path.resolve(helper_class_folder_path, "helper_class.sc");
fs.writeFileSync(
helper_class_file_path,
"TSTOSC__ObjectLiteral\n{\n var storing_dict;\n\n *new\n { |object_literal|\n ^super.new.initTSTOSC_PlainObject(object_literal)\n }\n\n initTSTOSC_PlainObject\n { |object_literal|\n storing_dict = object_literal;\n ^this\n }\n\n at\n { |selector|\n if (storing_dict.includesKey(selector),\n {\n var val = storing_dict.at(selector);\n if (val.isKindOf(Function),\n { ^{ |...a| val.valueArray([this] ++ a) ; } ; },\n { ^val ; }\n ) ;\n },\n { nil ; }\n ) ;\n }\n}"
);
}
main();