76 lines
2.9 KiB
Go
76 lines
2.9 KiB
Go
package runtime
|
||
|
||
import (
|
||
"archive/zip"
|
||
"bytes"
|
||
"encoding/json"
|
||
"fmt"
|
||
)
|
||
|
||
// BuildJSDeployZip упаковывает Node.js код в zip для Fission deployment package.
|
||
//
|
||
// Почему zip, а не просто literal-код:
|
||
// Fission node-env v3 поддерживает ESM модули. Чтобы Node.js трактовал файл
|
||
// как ESM, нужен package.json с {"type":"module"} рядом с main.js.
|
||
// Без него `import` синтаксис вызовет "require is not defined" или SyntaxError.
|
||
//
|
||
// Почему new Function:
|
||
// Пользовательский код пишет CJS-стиль (module.exports = ...) но мы хотим ESM wrapper.
|
||
// new Function() изолирует module/exports от глобального ESM контекста и позволяет
|
||
// исполнять CJS-код без изменений.
|
||
//
|
||
// Результат: zip с двумя файлами:
|
||
// - package.json: {"type":"module"}
|
||
// - main.js: ESM wrapper + инлайн пользовательский код через new Function
|
||
func BuildJSDeployZip(code string) ([]byte, error) {
|
||
// Сериализуем пользовательский код в JSON строку чтобы безопасно инлайнить
|
||
// в JavaScript-литерал — экранирует кавычки, переводы строк, спецсимволы.
|
||
codeJSON, err := json.Marshal(code)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("marshal user code: %w", err)
|
||
}
|
||
|
||
// main.js — ESM wrapper:
|
||
// 1. new Function создаёт функцию в пустом модульном контексте (нет import/export)
|
||
// 2. Передаём ей module и exports как параметры → пользовательский CJS код работает
|
||
// 3. Экспортируем default async function для Fission node-env entrypoint
|
||
wrapper := fmt.Sprintf(`const __mod = { exports: {} };
|
||
(new Function('module', 'exports', %s))(__mod, __mod.exports);
|
||
const _fn = __mod.exports;
|
||
|
||
export default async function(ctx) {
|
||
const fn = typeof _fn === 'function' ? _fn : (_fn.default || _fn.handler || _fn.main);
|
||
if (!fn) throw new Error('no exported function found in user code');
|
||
const result = await fn(ctx);
|
||
if (!result) return { status: 200, body: '' };
|
||
if (typeof result.status !== 'undefined') return result;
|
||
return { status: 200, ...result };
|
||
}
|
||
`, string(codeJSON))
|
||
|
||
var buf bytes.Buffer
|
||
zw := zip.NewWriter(&buf)
|
||
|
||
// package.json: включает ESM режим для всего архива
|
||
pkgfw, err := zw.Create("package.json")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if _, err := pkgfw.Write([]byte(`{"type":"module"}`)); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
fw, err := zw.Create("main.js")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if _, err := fw.Write([]byte(wrapper)); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
if err := zw.Close(); err != nil {
|
||
return nil, err
|
||
}
|
||
return buf.Bytes(), nil
|
||
}
|