UNPKG

tstosc

Version:

A transpiler that convert TypeScript to SuperCollider's SCLang.

183 lines (180 loc) 7.91 kB
#!/usr/bin/env node 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();