import path from 'node:path' import { glob } from "glob" import moment from 'moment' import { PlataBuildWasm } from './build_wasm' import { compileCode } from 'bytenode' import type { Plugin } from 'esbuild' export namespace PlataBuild { export const getConfigDefault: () => Build.Config = () => ({ env: 'prod', copyFolderToRoot: [], mapFiles: false, otimizar: true, plugins: [], }) export async function getBuildConfig(): PlataPromise { const config = await Plata.RequireAsync<{ default: unknown }>("@@/config.build") if (config.errorID !== undefined) { return PlataBuild.getConfigDefault() } const loadedConfig = await Plata.switchUnion((typeof config.exports.default) as 'function' | 'object', { _default: async (v) => Plata.BuildPlataError({ errorID: 'PLGETBLCONF001', msg: `Tipo de config não suportada ${v}` }), function: async () => (config.exports.default as () => unknown)(), object: async () => config.exports.default as unknown }) return loadedConfig } export function getScriptsByPlataType() { switch (Plata.ProjectJson['plata_type']) { case 'api': { return { start: 'plata-runtime --skip-recompile + plata-api' } } case 'cluster': { return { start: 'plata-runtime --skip-recompile + plata-cluster' } } default: { return {} } } } function bytenodePlugin(): Plugin { return { name: 'bytenode', setup(build) { const Module = require('node:module') // V8/Bytenode possui incompatibilidade conhecida com Arrow Functions ao gerar bytecode, // pois determinadas operações internas do V8 dependem da representação original da função. // Como o Bytenode serializa o código em bytecode e remove o source original, Arrow Functions // podem causar falhas em runtime. Forçamos a transformação de Arrow Functions para funções // tradicionais apenas no build Bytenode, preservando as demais features modernas do Node.js. build.initialOptions.supported = { ...(build.initialOptions.supported ?? {}), arrow: false, } build.onEnd(async (result) => { if (!result.outputFiles) return const files: typeof result.outputFiles = [] for (const file of result.outputFiles) { if (!file.path.endsWith('.js')) { files.push(file) continue } const jscFile = file.path.replace(/\.js$/, '.jsc') const content = await compileCode(Module.wrap(file.text), true) files.push({ contents: content as any, hash: file.hash, path: jscFile, text: content.toString('base64') }) } result.outputFiles = files }) } } } export async function compile(params: Build.CompilerConfigs) { const tsupRequire = await Plata.RequireAsync('tsup') if (tsupRequire.errorID !== undefined) { return tsupRequire } const files = [ ...glob.sync('*/**/*.+(ts|env|json)'), ...glob.sync('config.build.ts'), ].filter(f => { if (f.startsWith('node_modules')) return false ; if (f.startsWith('__')) return false ; if (f.endsWith('.d.ts')) return false ; if (params.packageJsonSetup) if (f === "config.build.ts") return false ; ; return true }) ; const { exports: tsup } = tsupRequire const outDir = path.join(params.basePath, '__BUILD__') const esbuildPlugins: Plugin[] = [] if (process.env.__PLATA_BYTENODE === '1') { esbuildPlugins.push(bytenodePlugin()) } await tsup.build({ outDir, tsconfig: path.join(Plata.ProjectDir, 'tsconfig.json'), clean: true, entry: files, skipNodeModulesBundle: true, shims: false, bundle: false, platform: 'node', target: 'node24', minify: params.otimizar === true, minifyIdentifiers: params.otimizar === true, minifySyntax: params.otimizar === true, minifyWhitespace: params.otimizar === true, sourcemap: params.mapFiles === true, silent: true, loader: { '.env': 'copy', '.json': 'copy', }, esbuildPlugins, esbuildOptions(options, context) { const labels = new Set([ ...(options.dropLabels ?? []), 'TYPE', 'PType', ...(params.dropLabels ?? []), ]) options.dropLabels = [...labels] }, }) console.log('✅ Codigo Compilado') const tools = new Plata.files.CliTools(Plata.ProjectDir, params.basePath) if (params.copyFolderToRoot !== undefined) { for (let index = 0; index < params.copyFolderToRoot.length; index++) { const folder = params.copyFolderToRoot[index]; await tools.copyFolderToDest(folder) console.log(`✅ Pasta ${folder} copiada`) } } if (params.plugins?.length) { for (let i = 0; i < params.plugins.length; i++) { const plugin = params.plugins[i] if (plugin.posBuild !== undefined) { console.log(`♻️ Rodando o plugin: ${plugin.name}`) await plugin.posBuild(params, outDir) } } console.log(`✅ Todos os plugins rodados`) } if (params.packageJsonSetup === true) { await tools.createFileToDest('package.json', JSON.stringify({ name: Plata.ProjectJson.name, version: moment().format('YYYYMMDD.HHmmss'), description: "", scripts: { ...Plata.ProjectJson['my-scripts'], ...PlataBuild.getScriptsByPlataType(), }, keywords: [], author: "", plata_skip_recompile: true, plata_type: Plata.ProjectJson['plata_type'], plata_name: Plata.ProjectJson['plata_name'], plata_no_setup: true, plata_build_env: params.env ?? 'prod', dependencies: { ...Plata.ProjectJson.dependencies } }, null, 4)) console.log(`🚀 Compilação finalizada`) } await PlataBuildWasm.compileWasm(outDir) return true } }