UNPKG

imba

Version:

Intuitive and powerful language for building webapps that fly

211 lines (193 loc) 6.04 kB
import { CompileOptions, ResolvedOptions } from './options'; import {compile} from 'imba/compiler' import nfs from 'node:fs' // @ts-ignore import { createMakeHot } from 'imba-hmr'; import { ImbaRequest } from './id'; import { safeBase64Hash } from './hash'; import { log } from './log'; import Cache from '../../bundler/cache.imba'; const scriptLangRE = /<script [^>]*lang=["']?([^"' >]+)["']?[^>]*>/; const _createCompileImba = (imbaConfig, makeHot?: Function) => async function compileImba( imbaRequest: ImbaRequest, code: string, options: Partial<ResolvedOptions> ): Promise<CompileData> { const { filename, normalizedFilename, cssId, ssr } = imbaRequest; const { emitCss = true } = options; const dependencies = []; // todo maybe: generate unique short references for all unique paths, cache them between runs, and send those in via sourceId const compileOptions: CompileOptions = { ...options.compilerOptions, filename, generate: ssr ? 'ssr' : 'dom', format: 'esm', resolveColors: true, sourcePath: filename, vite: true, sourcemap: options.compilerOptions.sourcemap ?? "extern", ...imbaConfig }; if (options.hot && options.emitCss) { const hash = `s-${safeBase64Hash(normalizedFilename)}`; log.debug(`setting cssHash ${hash} for ${normalizedFilename}`); compileOptions.cssHash = () => hash; } if (ssr && compileOptions.enableSourcemap !== false) { if (typeof compileOptions.enableSourcemap === 'object') { compileOptions.enableSourcemap.css = false; } else { compileOptions.enableSourcemap = { js: true, css: false }; } } // let preprocessed; // if (options.preprocess) { // try { // preprocessed = await preprocess(code, options.preprocess, { filename }); // } catch (e) { // e.message = `Error while preprocessing ${filename}${e.message ? ` - ${e.message}` : ''}`; // throw e; // } // if (preprocessed.dependencies) dependencies.push(...preprocessed.dependencies); // } // const finalCode = preprocessed ? preprocessed.code : code; // const finalCode = code const dynamicCompileOptions = await options.experimental?.dynamicCompileOptions?.({ filename, code, compileOptions }); if (dynamicCompileOptions && log.debug.enabled) { log.debug( `dynamic compile options for ${filename}: ${JSON.stringify(dynamicCompileOptions)}` ); } const finalCompileOptions = dynamicCompileOptions ? { ...compileOptions, ...dynamicCompileOptions } : compileOptions; finalCompileOptions.config = finalCompileOptions finalCompileOptions.styles = "extern" finalCompileOptions.platform = "browser" finalCompileOptions.vite = true const q = imbaRequest.query finalCompileOptions.platform = ssr ? "node": "browser" if(q.worker){ finalCompileOptions.platform = "worker" }else if(q.web){ finalCompileOptions.platform = "browser" } if(options.server?.config?.mode == "test" && options.server?.config?.test?.environment == 'node'){ finalCompileOptions.platform = "node" } // This is where you potentially cache it? // Maybe just add that function to the compiler directly const imbacache = globalThis.VITE_IMBA_CACHE; const parts = []; // match things in code const cachekey = `${filename}-${finalCompileOptions.platform}-vite2`; const mtime = nfs.existsSync(filename) ? nfs.statSync(filename).mtimeMs : 0; const wrap_compile = (code,o) => { const res = compile(code, o); const map = res.sourcemap; return { js: {code: res.js, dependencies: [], map}, css: {code: res.css}, warnings: res.warnings, errors: res.errors } } // Should move into the compiler itself // const compiled = await imbacache.memo(cachekey,mtime,()=> Promise.resolve(wrap_compile(code,finalCompileOptions))); const compiled = wrap_compile(code,finalCompileOptions); if (compiled["erroredΦ"]){ throw compiled } // const map = compiled.sourcemap // compiled.js = {code: compiled.js, map} // compiled.css = {code: compiled.css} if (emitCss && compiled.css.code) { // TODO properly update sourcemap? compiled.js.code += `\n/*__css_import__*/import ${JSON.stringify(cssId)};\n`; } // https://vitejs.dev/guide/api-plugin.html#handlehotupdate // only apply hmr when not in ssr context and hot options are set if (!ssr && makeHot) { compiled.js.code = makeHot({ id: filename, compiledCode: compiled.js.code, hotOptions: options.hot, compiled, originalCode: code, compileOptions: finalCompileOptions }); } // Dependencies will always be blank? compiled.js.dependencies = dependencies; return { filename, normalizedFilename, lang: code.match(scriptLangRE)?.[1] || 'js', // @ts-ignore compiled, ssr, dependencies }; }; // function buildMakeHot(options: ResolvedOptions) { // const needsMakeHot = options.hot !== false && options.isServe && !options.isProduction; // if (needsMakeHot) { // // @ts-ignore // const hotApi = options?.hot?.hotApi; // // @ts-ignore // const adapter = options?.hot?.adapter; // return createMakeHot({ // walk, // hotApi, // adapter, // hotOptions: { noOverlay: true, ...(options.hot as object) } // }); // } // } export function createCompileImba(options: ResolvedOptions, imbaConfig) { // const makeHot = buildMakeHot(options); return _createCompileImba(imbaConfig); } export interface Code { code: string; map?: any; dependencies?: any[]; } export interface Compiled { js: Code; css: Code; ast: any; // TODO type warnings: any[]; // TODO type vars: { name: string; export_name: string; injected: boolean; module: boolean; mutated: boolean; reassigned: boolean; referenced: boolean; writable: boolean; referenced_from_script: boolean; }[]; stats: { timings: { total: number; }; }; } export interface CompileData { filename: string; normalizedFilename: string; lang: string; compiled: Compiled; ssr: boolean | undefined; dependencies: string[]; }