import ts from 'typescript'; import fs from 'fs-extra'; import ora from 'ora'; import { glob } from 'glob'; import path from 'path'; import execa from 'execa'; async function buildTailchatServer() { const spinner = ora({ prefixText: 'Tailchat Server', }); try { spinner.start('Start compiling'); await fs.remove('./dist'); spinner.info('Compiling TS code'); await compileTsCode(); spinner.info('Moving static resource files'); await Promise.all([ fs.copy('./public', './dist/public', { recursive: true }), fs.copy('./locales', './dist/locales', { recursive: true }), fs.copy('./views', './dist/views', { recursive: true }), fs.copy( './services/openapi/oidc/views', './dist/services/openapi/oidc/views', { recursive: true } ), ]); if (process.platform !== 'win32' || (await isAdmin())) { spinner.info('Building plugin dependent symlink'); const nodeModulesList = await glob('./plugins/*/node_modules/*'); for (const item of nodeModulesList) { const src = path.resolve(__dirname, '../', item); const dest = path.resolve(__dirname, '../dist', item); spinner.text = `Building Symlink: ${src} -> ${dest}`; await fs.createSymlink(src, dest, 'dir'); } } else { spinner.warn( 'You are run command in windows without admin permit, create symlink will be skip' ); } spinner.succeed('Compiled!'); } catch (e) { console.error(e); spinner.fail('Compilation failed!'); } } buildTailchatServer(); /** * 编译Ts代码 * * tsc的api实现 */ function compileTsCode(): Promise { function compile( fileNames: string[], options: ts.CompilerOptions ): Promise { return new Promise((resolve, reject) => { const program = ts.createProgram(fileNames, options); const emitResult = program.emit(); const allDiagnostics = ts .getPreEmitDiagnostics(program) .concat(emitResult.diagnostics); allDiagnostics.forEach((diagnostic) => { if (diagnostic.file) { const { line, character } = ts.getLineAndCharacterOfPosition( diagnostic.file, diagnostic.start! ); const message = ts.flattenDiagnosticMessageText( diagnostic.messageText, '\n' ); console.log( `${diagnostic.file.fileName} (${line + 1},${ character + 1 }): ${message}` ); } else { console.log( ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n') ); } }); const exitCode = emitResult.emitSkipped ? 1 : 0; if (exitCode === 0) { resolve(); } else { reject(); } }); } const configPath = ts.findConfigFile( process.cwd() /*searchPath*/, ts.sys.fileExists, 'tsconfig.json' ); const configFile = ts.readJsonConfigFile(configPath, ts.sys.readFile); const { fileNames, options } = ts.parseJsonSourceFileConfigFileContent( configFile, ts.sys, process.cwd() ); return compile(fileNames, options); } /** * check is window admin */ async function isAdmin() { if (process.platform !== 'win32') { return false; } try { // https://stackoverflow.com/a/21295806/1641422 await execa('fsutil', ['dirty', 'query', process.env.systemdrive]); return true; } catch (error) { if (error.code === 'ENOENT') { return testFltmc(); } return false; } } async function testFltmc() { try { await execa('fltmc'); return true; } catch { return false; } }