UNPKG

@wshcmx/cli

Version:

Набор CLI-инструментов предназначен для автоматизации сборки и отслеживания изменений в исходном коде с последующей транспиляцией в синтаксис WebSoftHCM.

139 lines (138 loc) 6.17 kB
import fs from 'node:fs'; import { dirname, normalize, relative, resolve } from 'node:path'; import ts from 'typescript'; import { enumsToObjects } from '../transformers/enums_to_objects.js'; import { removeExports } from '../transformers/remove_exports.js'; import { convertTemplateStrings } from '../transformers/template_strings.js'; import { transformNamespaces } from '../transformers/transform_namespaces.js'; import { args, ArgsFlags } from './args.js'; import { logger } from './logger.js'; export function buildTypescriptFiles(configuration) { const program = ts.createProgram(configuration.fileNames, configuration.options); const host = ts.createCompilerHost(program.getCompilerOptions()); decorateHostWriteFile(host); const emitResult = decorateProgramEmit(host, program); const diagnostics = [ ...ts.getPreEmitDiagnostics(program), ...emitResult.diagnostics ]; diagnostics.forEach(diagnostic => { if (diagnostic.file) { const { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start); const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); logger.error(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`); } else { logger.error(ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')); } }); return emitResult; } export function collectNonTypescriptFiles(configuration) { const { outDir } = configuration.options; if (outDir === undefined) { throw new Error('The outDir option is not set in the tsconfig.json file.'); } if (process.versions.node.split('.')[0] < '22') { throw new Error('The watch mode for non TypeScript files is available only since Node.js v22'); } const { exclude, files, include } = configuration.raw; const fileNames = configuration.fileNames.map(normalize); const normalizedExclude = (exclude ?? []).map(normalize); return fs.globSync([...(include ?? []), ...(files ?? [])]) .filter(x => !fileNames.includes(x)) .filter(x => !normalizedExclude?.includes(x)) .filter(x => fs.statSync(x).isFile()); } function reportDiagnostic(diagnostic) { if (diagnostic.file) { const { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start); const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); logger.error(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`); } else { logger.error(ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')); } } function decorateHostWriteFile(host) { const originalWriteFile = host.writeFile; host.writeFile = (fileName, data, writeByteOrderMark, onError, sourceFiles) => { if (fileName.endsWith('.js')) { // Convert namespaces if (data.indexOf('"META:NAMESPACE:') !== -1) { fileName = fileName.replace('.js', '.bs'); } // Add aspnet render tag if (data.indexOf('/// @html') !== -1) { data = `<%\n// <script>\n${data}\n%>`; fileName = fileName.replace('.js', '.html'); } if (!args.has(ArgsFlags.RETAIN_NON_ASCII_CHARACTERS)) { // Decode non ASCII characters data = data.replace(/\\u[\dA-Fa-f]{4}/g, (match) => { return String.fromCharCode(parseInt(match.substr(2), 16)); }); } } originalWriteFile(fileName, data, writeByteOrderMark, onError, sourceFiles); }; } function decorateProgramEmit(host, program) { return program?.emit(undefined, host.writeFile, undefined, undefined, { before: [ removeExports(), enumsToObjects(), convertTemplateStrings(), transformNamespaces() ], }); } function reportWatchStatusChanged(diagnostic) { console.info(ts.formatDiagnostic(diagnostic, { getCanonicalFileName: path => path, getCurrentDirectory: ts.sys.getCurrentDirectory, getNewLine: () => ts.sys.newLine, })); } export function watchTypescriptFiles(configuration) { const host = ts.createWatchCompilerHost(configuration.fileNames, configuration.options, ts.sys, ts.createEmitAndSemanticDiagnosticsBuilderProgram, reportDiagnostic, reportWatchStatusChanged); const origCreateProgram = host.createProgram; host.createProgram = (rootNames = [], options, host, oldProgram) => { decorateHostWriteFile(host); const program = origCreateProgram(rootNames, options, host, oldProgram); decorateProgramEmit(host, program); return program; }; ts.createWatchProgram(host); } export function watchNonTypescriptFiles(configuration) { if (!args.has(ArgsFlags.INCLUDE_NON_TS_FILES)) { return; } const { rootDir, outDir } = configuration.options; const entries = collectNonTypescriptFiles(configuration); entries.forEach(x => { const filePath = rootDir ? relative(rootDir, x) : x; const outputFilePath = resolve(outDir, filePath); fs.watch(resolve(x), (event) => { if (event == 'change') { fs.mkdirSync(dirname(outputFilePath), { recursive: true }); fs.writeFileSync(outputFilePath, fs.readFileSync(resolve(x), 'utf-8')); logger.success(`🔨 ${new Date().toLocaleTimeString()} File ${x} has been changed`); } }); }); } export function buildNonTypescriptFiles(configuration) { if (!args.has(ArgsFlags.INCLUDE_NON_TS_FILES)) { return; } const { rootDir, outDir } = configuration.options; const entries = collectNonTypescriptFiles(configuration); entries.forEach(x => { const filePath = rootDir ? relative(rootDir, x) : x; const outputFilePath = resolve(outDir, filePath); fs.mkdirSync(dirname(outputFilePath), { recursive: true }); fs.writeFileSync(outputFilePath, fs.readFileSync(resolve(x), 'utf-8')); }); }