UNPKG

pcf-scripts

Version:

This package contains a module for building PowerApps Component Framework (PCF) controls. See project homepage how to install.

151 lines (149 loc) 7.02 kB
"use strict"; // Copyright (C) Microsoft Corporation. All rights reserved. Object.defineProperty(exports, "__esModule", { value: true }); exports.getNamespaceStub = getNamespaceStub; exports.generateStub = generateStub; exports.getWebpackConfig = getWebpackConfig; const fs = require("node:fs"); const path = require("node:path"); const webpack_1 = require("webpack"); const webpack_merge_1 = require("webpack-merge"); const constants = require("./constants"); const featureManager_1 = require("./featureManager"); const platformLibrariesHandler_1 = require("./platformLibrariesHandler"); const buildConfig_1 = require("./buildConfig"); const featureMgr = new featureManager_1.FeatureManager(); const buildConfig = (0, buildConfig_1.getBuildConfig)(); // Append a stub to webpack bundle to prevent overwriting global variables // If different controls are using the same namespace, webpack will keep redeclaring // the namespace as global variables. As a result, only of one the controls can be called. // The inserted stub checks whether the namespace already exists and uses a temporary variable // to hold the control's constructor. function getNamespaceStub(namespace, constructor) { const splitNamespace = namespace.split("."); let stub = `\tvar ${splitNamespace[0]} = ${splitNamespace[0]} || {};\n`; for (let i = 1; i < splitNamespace.length; i++) { const littleStub = `${splitNamespace.slice(0, i + 1).join(".")}`; stub += `\t${littleStub} = ${littleStub} || {};\n`; } stub = stub + `\t${namespace}.${constructor} = ${constants.TEMP_NAMESPACE}.${constructor};\n` + `\t${constants.TEMP_NAMESPACE} = undefined;\n`; return stub; } // Use registration function if exists, else fall back to the stub that uses the namespace as a global variable function generateStub(namespace, constructor) { return ("\nif (window.ComponentFramework && window.ComponentFramework.registerControl) {\n" + `\tComponentFramework.registerControl('${namespace}.${constructor}', ${constants.TEMP_NAMESPACE}.${constructor});\n` + `} else {\n${getNamespaceStub(namespace, constructor)}}`); } function getWebpackConfig(control, controlOutputDir, buildMode, watchFlag) { const entryPoint = path.resolve(control.getControlPath(), control.getCodeRelativePath()); let customConfig = {}; const customConfigPath = path.resolve(control.getControlPath(), "..", constants.WEBPACK_CUSTOMIZATION_FILE_NAME); if (featureMgr.isFeatureEnabled("pcfAllowCustomWebpack") && fs.existsSync(customConfigPath)) { // eslint-disable-next-line @typescript-eslint/no-require-imports customConfig = require(customConfigPath); } const allowProjectReferences = featureMgr.isFeatureEnabled("pcfAllowProjectReferences"); const babelLoader = getBabelLoader(buildMode === "production" ? true : "auto"); const oobConfig = { // `production` mode will minify, while `development` will optimize for debugging. mode: buildMode, watch: watchFlag, watchOptions: { aggregateTimeout: 500, ignored: /node_modules/, }, // Tells webpack where to start walking the graph of dependencies entry: entryPoint, output: { // This library value control what global variable the output control is placed in. library: constants.TEMP_NAMESPACE, pathinfo: true, filename: constants.BUNDLE_NAME, path: controlOutputDir, }, resolve: { // Tell webpack which extensions to try when it is looking for a file. extensions: [".ts", ".tsx", ".mts", ".js", ".jsx", ".mjs", ".svg"], }, module: { rules: [ { // Tells webpack how to load files with TS, TSX, MTS, or MTSX extensions. test: /\.(tsx?|mtsx?)$/, use: [ babelLoader, { loader: require.resolve("ts-loader"), options: { allowTsInNodeModules: true, projectReferences: allowProjectReferences, }, }, ], exclude: (tsPath) => { // PNPM places relative imports in `node_modules`. // These files should NOT be excluded from the loader. // Format: node_modules/.pnpm/.../some/path/...@file+... if (/node_modules.\.pnpm.+@file\+/.test(tsPath)) { return false; } return tsPath.includes("node_modules"); }, }, { // Tell webpack how to handle JS, JSX, MJS, or MJSX files test: /\.(jsx?|mjsx?)$/, use: [babelLoader], }, { test: /\.css$/, use: ["style-loader", "css-loader", "sass-loader"], }, { test: /\.svg$/, use: ["@svgr/webpack"], }, ], }, plugins: [ new webpack_1.optimize.LimitChunkCountPlugin({ // prevent creating split bundles, since the PCF runtime cannot handle chunked bundles // neither does the control manifest and our tooling have support to build and package chunked bundles (e.g. no SoPa support) maxChunks: 1, }), new webpack_1.WatchIgnorePlugin({ paths: [/\.js$/, /\.d\.[cm]?ts$/], }), ], }; if (featureMgr.isFeatureEnabled("pcfReactControls") && (0, platformLibrariesHandler_1.hasPlatformLibs)(control)) { const platformLibrariesHandler = new platformLibrariesHandler_1.PlatformLibrariesHandler(); const externalsForPlatformLibs = platformLibrariesHandler.getLatestVersions(control); oobConfig.externals = externalsForPlatformLibs; } return (0, webpack_merge_1.merge)(oobConfig, customConfig); } // Some babel plugins to support modern JS and TypeScript. const babelPlugins = [[require.resolve("@babel/plugin-proposal-decorators"), { version: "2023-11" }]]; // Config for babel to tell it about which browsers we are targeting. const babelPresetEnv = [ require.resolve("@babel/preset-env"), { targets: buildConfig?.targets ?? { esmodules: true, }, }, ]; function getBabelLoader(compact = "auto") { return { loader: require.resolve("babel-loader"), options: { sourceType: "unambiguous", presets: [babelPresetEnv, [require.resolve("@babel/preset-react")]], plugins: babelPlugins, compact, }, }; } //# sourceMappingURL=webpackConfig.js.map