pcf-scripts
Version:
This package contains a module for building PowerApps Component Framework (PCF) controls. See project homepage how to install.
178 lines (176 loc) • 8.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CompileTaskForWatch = exports.CompileTask = void 0;
const fs = require("fs-extra");
const path = require("node:path");
const webpack_1 = require("webpack");
const constants = require("../constants");
const diagnosticMessages_generated_1 = require("../diagnosticMessages.generated");
const locale_1 = require("../generated/locale");
const pcfFileWatcher_1 = require("../pcfFileWatcher");
const webpackConfig_1 = require("../webpackConfig");
class CompileTask {
constructor() {
this._options = {};
}
getDescription() {
return (0, locale_1.translate)(diagnosticMessages_generated_1.strings.task_compile_and_bundle.key);
}
setOptions(options) {
this._options = { ...this._options, ...options };
}
run(context) {
return this.runBuild(context, false);
}
runBuild(context, isWatch, maxBundleSizeInBytes) {
if (context.shouldUseESBuild()) {
if (maxBundleSizeInBytes) {
this.addError(diagnosticMessages_generated_1.strings.esbuild_max_bundle_size_not_supported, context.getDiagnostic());
return Promise.reject(new Error(diagnosticMessages_generated_1.strings.esbuild_max_bundle_size_not_supported.message));
}
return this.runESBuild(context, isWatch);
}
return this.runWebpackBuild(context, isWatch, maxBundleSizeInBytes);
}
runESBuild(context, isWatch) {
const buildMode = context.getBuildMode();
const outDir = context.getOutDir();
if (!outDir) {
context.getDiagnostic().push(diagnosticMessages_generated_1.strings.buildconfig_no_outdir);
return Promise.reject(new Error());
}
return context.mapControls(async (control) => {
const esbuild = await Promise.resolve().then(() => require("esbuild"));
const controlOutputDir = path.resolve(outDir, control.getControlFolderName());
const bundlePath = path.join(controlOutputDir, constants.BUNDLE_NAME);
fs.ensureDirSync(controlOutputDir);
if (isWatch) {
// remove old errors, only needed when watching
const diag = context.getDiagnostic();
diag.clear();
}
// The link below points to the TS schema for ESBuild's BuildOptions object:
// https://github.com/evanw/esbuild/blob/main/lib/shared/types.ts#L113
const globalName = constants.TEMP_NAMESPACE;
const buildOptions = {
entryPoints: [`./${control.getControlPath()}`],
bundle: true,
outfile: bundlePath,
sourcemap: buildMode === "development",
minify: buildMode === "production",
write: true,
treeShaking: true,
globalName,
loader: {
[".svg"]: "dataurl",
},
};
const build = await esbuild.build(buildOptions);
const stub = (0, webpackConfig_1.generateStub)(control.getControlNamespace(), control.getControlName());
fs.appendFileSync(bundlePath, stub, "utf8");
return build;
});
}
runWebpackBuild(context, isWatch, maxBundleSizeInBytes) {
const buildMode = context.getBuildMode();
const outDir = context.getOutDir();
if (!outDir) {
context.getDiagnostic().push(diagnosticMessages_generated_1.strings.buildconfig_no_outdir);
return Promise.reject(new Error());
}
return context.mapControls((control) => {
const controlOutputDir = path.resolve(outDir, control.getControlFolderName());
const config = (0, webpackConfig_1.getWebpackConfig)(control, controlOutputDir, buildMode, isWatch);
const bundlePath = path.join(controlOutputDir, constants.BUNDLE_NAME);
const bundleSizeInBytes = typeof maxBundleSizeInBytes === "number" && maxBundleSizeInBytes > 0 ? maxBundleSizeInBytes : constants.MAX_BUNDLE_SIZE_IN_MB * 1024 * 1024;
fs.ensureDirSync(controlOutputDir);
return new Promise((resolve, reject) => (0, webpack_1.webpack)(config, (error, stats) => {
let colorize = false;
let toReject = false;
const diag = context.getDiagnostic();
if (this._options?.colorize) {
colorize = true;
}
if (!isWatch || error || stats?.hasErrors()) {
console.log(`[Webpack ${(0, locale_1.translate)(diagnosticMessages_generated_1.strings.statistics.key)}]:\n${stats?.toString({ chunks: false, colors: colorize })}`);
}
if (error) {
this.addError(diagnosticMessages_generated_1.strings.build_error_generic, diag, error.message);
toReject = true;
}
else if (stats?.hasErrors()) {
this.addError(diagnosticMessages_generated_1.strings.bundling_error, diag);
toReject = true;
}
else if (buildMode === "production") {
let assets = stats?.toJson().assets;
if (assets) {
assets = assets.filter((obj) => obj.name === constants.BUNDLE_NAME);
// webpack stats object uses byte as unit
if (assets.length > 0 && assets[0].size > bundleSizeInBytes) {
this.addError(diagnosticMessages_generated_1.strings.bundle_size_exceeds_max, diag, (bundleSizeInBytes / (1024 * 1024)).toString());
toReject = true;
}
}
}
if (!control.getControlManifest().getManifestData().manifest) {
this.addError(diagnosticMessages_generated_1.strings.generating_stub_error, diag);
toReject = true;
}
// If there are no other errors, check that the bundle file exists
// If the bundle is missing even though webpack completed without errors, that could mean that the webpack config was misconfigured.
if (!fs.existsSync(bundlePath) && !toReject) {
this.addError(diagnosticMessages_generated_1.strings.bundle_not_found, diag, bundlePath);
toReject = true;
}
if (toReject) {
return reject(new Error(diagnosticMessages_generated_1.strings.bundling_error.message));
}
if (isWatch) {
diag.clear();
} // remove old errors, only needed when watching
const stub = (0, webpackConfig_1.generateStub)(control.getControlNamespace(), control.getControlName());
fs.appendFileSync(bundlePath, stub, "utf8");
return resolve({ compileTime: stats?.toJson().time });
}));
});
}
// In watch mode: need to check for duplication of errors in case they were not fixed from last save
addError(error, diag, errorMessage) {
let lastEntry;
let secondToLastEntry;
if (diag.length() > 0) {
lastEntry = diag.peek();
if (diag.length() > 1) {
secondToLastEntry = diag.peek(diag.length() - 2);
}
}
if (secondToLastEntry && lastEntry) {
if (error.code !== secondToLastEntry.diag.code && error.code !== lastEntry.diag.code) {
errorMessage ? diag.pushA(error, [errorMessage]) : diag.push(error);
}
}
else if (lastEntry) {
if (error.code !== lastEntry.diag.code) {
errorMessage ? diag.pushA(error, [errorMessage]) : diag.push(error);
}
}
else {
errorMessage ? diag.pushA(error, [errorMessage]) : diag.push(error);
}
}
}
exports.CompileTask = CompileTask;
class CompileTaskForWatch extends CompileTask {
run(context) {
return this.runBuild(context, false).then(() => {
const fileWatcher = new pcfFileWatcher_1.PCFFileWatcher();
// eslint-disable-next-line @typescript-eslint/no-floating-promises
fileWatcher.start(context);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.runBuild(context, true);
});
}
}
exports.CompileTaskForWatch = CompileTaskForWatch;
//# sourceMappingURL=compileTask.js.map