const path = require("path"); const fs = require("fs"); const webpack = require("webpack"); const resolve = require("path").resolve; const CertStore = require("@microsoft/gulp-core-build-serve/lib/CertificateStore"); const CertificateStore = CertStore.CertificateStore || CertStore.default; const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin"); const del = require("del"); const port = 4321; const host = "https://localhost:" + port; /// // Transforms define("", ...) to web part specific define(" { for (const assetId in this.opitons.modulesMap) { const moduleMap = this.opitons.modulesMap[assetId]; if (compilation.assets[assetId]) { const rawValue = compilation.assets[assetId].children[0]._value; compilation.assets[assetId].children[0]._value = rawValue.replace(this.opitons.libraryName, moduleMap.id + "_" + moduleMap.version); } } }); } } /// // Removes *.module.scss.ts on the first execution in order prevent conflicts with *.module.scss.d.ts // generated by css-modules-typescript-loader /// class ClearCssModuleDefinitionsPlugin { constructor(options) { this.options = options || {}; } apply(compiler) { compiler.hooks.done.tap("FixStylesPlugin", stats => { if (!this.options.deleted) { setTimeout(() => { del.sync(["src/**/*.module.scss.ts"]); }, 3000); this.options.deleted = true; } }); } } let baseConfig = { target: "web", mode: "development", devtool: "source-map", resolve: { extensions: [".ts", ".tsx", ".js"], modules: ["node_modules"] }, context: path.resolve(__dirname), module: { rules: [ { test: /\.tsx?$/, loader: "ts-loader", options: { transpileOnly: true, compilerOptions: { declarationMap: false } }, exclude: /node_modules/ }, { use: [{ loader: "@microsoft/loader-cased-file", options: { name: "[name:lower]_[hash].[ext]" } }], test: /\.(jpe?g|png|woff|eot|ttf|svg|gif|dds)$/i }, { use: [{ loader: "html-loader" }], test: /\.html$/ }, { test: /\.css$/, use: [ { loader: "@microsoft/loader-load-themed-styles", options: { async: true } }, { loader: "css-loader" } ] }, { test: function (fileName) { return fileName.endsWith(".module.scss"); // scss modules support }, use: [ { loader: "@microsoft/loader-load-themed-styles", options: { async: true } }, "css-modules-typescript-loader", { loader: "css-loader", options: { modules: { localIdentName: "[local]_[hash:base64:8]" } } }, // translates CSS into CommonJS "sass-loader" // compiles Sass to CSS, using Node Sass by default ] }, { test: function (fileName) { return !fileName.endsWith(".module.scss") && fileName.endsWith(".scss"); // just regular .scss }, use: [ { loader: "@microsoft/loader-load-themed-styles", options: { async: true } }, "css-loader", // translates CSS into CommonJS "sass-loader" // compiles Sass to CSS, using Node Sass by default ] } ] }, plugins: [ new ForkTsCheckerWebpackPlugin({ tslint: true }), new ClearCssModuleDefinitionsPlugin(), new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV), "process.env.DEBUG": JSON.stringify(true), "DEBUG": JSON.stringify(true) })], devServer: { hot: false, contentBase: resolve(__dirname), publicPath: host + "/dist/", host: "localhost", port: port, disableHostCheck: true, historyApiFallback: true, open: true, writeToDisk: false, openPage: host + "/temp/workbench.html", stats: { preset: "errors-only", colors: true, chunks: false, modules: false, assets: false }, proxy: { // url re-write for resources to be served directly from src folder "/lib/**/loc/*.js": { target: host, pathRewrite: { "^/lib": "/src" }, secure: false } }, headers: { "Access-Control-Allow-Origin": "*", }, https: { cert: CertificateStore.instance.certificateData, key: CertificateStore.instance.keyData } }, } const createConfig = function () { // remove old css module TypeScript definitions del.sync(["dist/*.js", "dist/*.map"]); // we need only "externals", "output" and "entry" from the original webpack config let originalWebpackConfig = require("./temp/_webpack_config.json"); baseConfig.externals = originalWebpackConfig.externals; baseConfig.output = originalWebpackConfig.output; baseConfig.entry = getEntryPoints(originalWebpackConfig.entry); baseConfig.output.publicPath = host + "/dist/"; const manifest = require("./temp/manifests.json"); const modulesMap = {}; const originalEntries = Object.keys(originalWebpackConfig.entry); for (const jsModule of manifest) { if (jsModule.loaderConfig && jsModule.loaderConfig.entryModuleId && originalEntries.indexOf(jsModule.loaderConfig.entryModuleId) !== -1) { modulesMap[jsModule.loaderConfig.entryModuleId + ".js"] = { id: jsModule.id, version: jsModule.version } } } baseConfig.plugins.push(new DynamicLibraryPlugin({ modulesMap: modulesMap, libraryName: originalWebpackConfig.output.library })); return baseConfig; } function getEntryPoints(entry) { // fix: ".js" entry needs to be ".ts" // also replaces the path form /lib/* to /src/* let newEntry = {}; let libSearchRegexp; if (path.sep === "/") { libSearchRegexp = /\/lib\//gi; } else { libSearchRegexp = /\\lib\\/gi; } const srcPathToReplace = path.sep + "src" + path.sep; for (const key in entry) { let entryPath = entry[key]; if (entryPath.indexOf("bundle-entries") === -1) { entryPath = entryPath.replace(libSearchRegexp, srcPathToReplace).slice(0, -3) + ".ts"; } else { // replace paths and extensions in bundle file let bundleContent = fs.readFileSync(entryPath).toString(); bundleContent = bundleContent.replace(libSearchRegexp, srcPathToReplace).replace(/\.js/gi, ".ts"); fs.writeFileSync(entryPath, bundleContent); } newEntry[key] = entryPath; } return newEntry; } module.exports = createConfig();