@kitchenshelf/serverless-rspack
Version:
[Serverless Framework](https://www.serverless.com) plugin for zero-config JavaScript and TypeScript code bundling using the high performance Rust-based JavaScript bundler [`rspack`](https://rspack.dev/guide/start/introduction)
153 lines • 7.72 kB
JavaScript
import { __awaiter } from "tslib";
import { assert } from 'node:console';
import { readdirSync } from 'node:fs';
import { rm } from 'node:fs/promises';
import path from 'node:path';
import { bundle } from './bundle.js';
import { SERVERLESS_FOLDER, WORK_FOLDER } from './constants.js';
import { determineFileParts, enabledViaConfigObject, enabledViaSimpleConfig, isNodeFunction, } from './helpers.js';
import { AfterDeployFunctionPackageFunction } from './hooks/deploy-function/after-package-function.js';
import { BeforeDeployFunctionPackageFunction } from './hooks/deploy-function/before-package-function.js';
import { Initialize } from './hooks/initialize.js';
import { BeforeInvokeLocalInvoke } from './hooks/invoke-local/before-invoke.js';
import { BeforeOfflineStartInit } from './hooks/offline/start-init.js';
import { AfterPackageCreateDeploymentArtifacts } from './hooks/package/after-create-deployment-artifacts.js';
import { BeforePackageCreateDeploymentArtifacts } from './hooks/package/before-create-deployment-artifacts.js';
import { pack } from './pack.js';
import { scripts } from './scripts.js';
import { PluginOptionsSchema, } from './types.js';
export class RspackServerlessPlugin {
constructor(serverless, options, logging) {
this.functionEntries = {};
this.functionScripts = {};
this.offlineMode = false;
this.timings = new Map();
this.bundle = bundle.bind(this);
this.pack = pack.bind(this);
this.scripts = scripts.bind(this);
assert(logging, 'Please use serverless V4');
serverless.configSchemaHandler.defineFunctionProperties('aws', {
properties: {
rspack: {
oneOf: [
{ type: 'boolean' },
{
type: 'object',
properties: {
enable: { type: 'boolean' },
scripts: { type: 'array', items: { type: 'string' } },
},
required: [],
},
],
},
},
});
this.serverless = serverless;
this.options = options;
this.log = logging.log;
this.serviceDirPath = this.serverless.config.serviceDir;
this.packageOutputFolder = SERVERLESS_FOLDER;
this.buildOutputFolder = WORK_FOLDER;
this.buildOutputFolderPath = path.join(this.serviceDirPath, this.buildOutputFolder);
this.hooks = {
initialize: Initialize.bind(this),
'before:package:createDeploymentArtifacts': BeforePackageCreateDeploymentArtifacts.bind(this),
'after:package:createDeploymentArtifacts': AfterPackageCreateDeploymentArtifacts.bind(this),
'before:deploy:function:packageFunction': BeforeDeployFunctionPackageFunction.bind(this),
'after:deploy:function:packageFunction': AfterDeployFunctionPackageFunction.bind(this),
'before:invoke:local:invoke': BeforeInvokeLocalInvoke.bind(this),
'before:offline:start:init': BeforeOfflineStartInit.bind(this),
};
}
buildFunctionEntries(functions) {
this.log.verbose(`[sls-rspack] Building function entries for: ${functions}`);
let entries = {};
functions.forEach((functionName) => {
const functionDefinitionHandler = this.serverless.service.getFunction(functionName);
if (this.isEnabledViaRspack(functionDefinitionHandler) ||
this.isEnabledNodeFunction(functionDefinitionHandler)) {
// TODO: support container images
const entry = this.getEntryForFunction(functionName, functionDefinitionHandler);
entries = Object.assign(Object.assign({}, entries), entry);
}
});
return entries;
}
buildFunctionScripts(functions) {
this.log.verbose(`[sls-rspack] Building function scripts for: ${functions}`);
const scripts = {};
functions.forEach((functionName) => {
const functionDefinitionHandler = this.serverless.service.getFunction(functionName);
if (functionDefinitionHandler.rspack &&
!enabledViaSimpleConfig(functionDefinitionHandler.rspack) &&
functionDefinitionHandler.rspack.enable !== false &&
functionDefinitionHandler.rspack.scripts) {
this.log.verbose(`[sls-rspack] Found ${functionDefinitionHandler.rspack.scripts.length} scripts for function ${functionName}`);
scripts[functionName] = functionDefinitionHandler.rspack.scripts;
}
});
return scripts;
}
cleanup() {
return __awaiter(this, void 0, void 0, function* () {
if (!this.pluginOptions.keepOutputDirectory) {
yield rm(path.join(this.buildOutputFolderPath), { recursive: true });
}
});
}
getPluginOptions() {
var _a, _b;
return PluginOptionsSchema.parse((_b = (_a = this.serverless.service.custom) === null || _a === void 0 ? void 0 : _a['rspack']) !== null && _b !== void 0 ? _b : {});
}
isEnabledNodeFunction(functionDefinitionHandler) {
return (isNodeFunction(functionDefinitionHandler, this.serverless.service.provider.runtime) && !this.isDisabledViaRspack(functionDefinitionHandler));
}
isDisabledViaRspack(functionDefinitionHandler) {
return (functionDefinitionHandler.rspack !== undefined &&
!enabledViaSimpleConfig(functionDefinitionHandler.rspack) &&
!enabledViaConfigObject(functionDefinitionHandler.rspack));
}
isEnabledViaRspack(functionDefinitionHandler) {
return (functionDefinitionHandler.rspack &&
(enabledViaSimpleConfig(functionDefinitionHandler.rspack) ||
enabledViaConfigObject(functionDefinitionHandler.rspack)));
}
getEntryForFunction(name, serverlessFunction) {
const handler = serverlessFunction.handler;
this.log.verbose(`[sls-rspack] Processing function ${name} with provided handler ${handler}`);
const handlerFile = this.getHandlerFile(handler);
const { filePath, fileName } = determineFileParts(handlerFile);
const safeFilePath = filePath ? '/' + filePath + '/' : '/';
const files = readdirSync(`./${filePath}`);
const file = files.find((file) => {
return file.startsWith(fileName);
});
if (!file) {
throw new this.serverless.classes.Error(`Unable to find file [${fileName}] in path: [./${filePath}]`);
}
const ext = path.extname(file);
this.log.verbose(`[sls-rspack] Determined: filePath: [${safeFilePath}] - fileName: [${fileName}] - ext: [${ext}]`);
const outputExtension = this.isESM() ? 'mjs' : 'js';
return {
[name]: {
import: `./${handlerFile}${ext}`,
filename: `[name]${safeFilePath}${fileName}.${outputExtension}`,
},
};
}
getHandlerFile(handler) {
// Check if handler is a well-formed path based handler.
const handlerEntry = /(.*)\..*?$/.exec(handler);
if (handlerEntry) {
return handlerEntry[1];
}
throw new this.serverless.classes.Error(`malformed handler: ${handler}`);
}
isESM() {
var _a, _b;
return (((_b = (_a = this.providedRspackConfig) === null || _a === void 0 ? void 0 : _a.experiments) === null || _b === void 0 ? void 0 : _b.outputModule) ||
this.pluginOptions.esm);
}
}
//# sourceMappingURL=serverless-rspack.js.map