UNPKG

@procore/core-scripts

Version:

A CLI to enhance your development experience

532 lines • 21.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.normalizePublicPath = void 0; exports.setupWebpack = setupWebpack; const tslib_1 = require("tslib"); const cdn_1 = tslib_1.__importDefault(require("@procore/cdn")); const case_sensitive_paths_webpack_plugin_1 = tslib_1.__importDefault(require("case-sensitive-paths-webpack-plugin")); const fs_1 = tslib_1.__importDefault(require("fs")); const html_webpack_plugin_1 = tslib_1.__importDefault(require("html-webpack-plugin")); const mini_css_extract_plugin_1 = tslib_1.__importDefault(require("mini-css-extract-plugin")); const webpack_bugsnag_plugins_1 = require("webpack-bugsnag-plugins"); const css_minimizer_webpack_plugin_1 = tslib_1.__importDefault(require("css-minimizer-webpack-plugin")); const path_1 = tslib_1.__importDefault(require("path")); const postcss_flexbugs_fixes_1 = tslib_1.__importDefault(require("postcss-flexbugs-fixes")); const postcss_normalize_1 = tslib_1.__importDefault(require("postcss-normalize")); const postcss_preset_env_1 = tslib_1.__importDefault(require("postcss-preset-env")); const ForkTsCheckerWebpackPlugin_1 = tslib_1.__importDefault(require("react-dev-utils/ForkTsCheckerWebpackPlugin")); const getCSSModuleLocalIdent_1 = tslib_1.__importDefault(require("react-dev-utils/getCSSModuleLocalIdent")); const InlineChunkHtmlPlugin_1 = tslib_1.__importDefault(require("react-dev-utils/InlineChunkHtmlPlugin")); const InterpolateHtmlPlugin_1 = tslib_1.__importDefault(require("react-dev-utils/InterpolateHtmlPlugin")); const ModuleNotFoundPlugin_1 = tslib_1.__importDefault(require("react-dev-utils/ModuleNotFoundPlugin")); const resolve_1 = tslib_1.__importDefault(require("resolve")); const terser_webpack_plugin_1 = tslib_1.__importDefault(require("terser-webpack-plugin")); const webpack_1 = tslib_1.__importDefault(require("webpack")); const webpack_bundle_analyzer_1 = require("webpack-bundle-analyzer"); const webpack_manifest_plugin_1 = require("webpack-manifest-plugin"); const workbox_webpack_plugin_1 = tslib_1.__importDefault(require("workbox-webpack-plugin")); const config_1 = require("../../babel/config"); const environment_1 = require("../../environment"); const moduleFederationDecorator_1 = require("./pluginDecorators/moduleFederationDecorator"); /** * Passing `https://assets%d.procore.com` as the public path breaks the chunks * since they are being injected by JavaScript. * * We need to replace `%d` with a valid value since this is done in * `Hydra::AssetPath`. */ const normalizePublicPath = (publicPath) => publicPath.replace(/%d/, '0'); exports.normalizePublicPath = normalizePublicPath; function getIndexHtmlPath(workspace) { const appTemplate = workspace.resolve('public', 'index.html'); const defaultTemplate = path_1.default.resolve(__dirname, '..', '..', '..', 'public', 'index.html'); return fs_1.default.existsSync(appTemplate) ? appTemplate : defaultTemplate; } const resolveCdn = (0, cdn_1.default)(4); const BROWSER_LIST = [ 'last 2 chrome versions', 'last 2 firefox versions', 'last 2 safari versions', 'last 2 edge versions', 'ie 11', ]; function isEnvValueTrue(envValue) { if (envValue) { return envValue.toLowerCase() === 'true'; } return false; } function setupWebpack(config, opts) { var _a, _b, _c, _d; const options = { shouldInlineRuntimeChunk: false, shouldUseSourceMap: true, ...opts, }; const isEnvDevelopment = options.env === 'development'; const isEnvProduction = options.env === 'production'; const appTsConfigPath = opts.workspace.resolve('tsconfig.json'); const appBuildPath = opts.workspace.resolve('build'); const appSrcPath = opts.workspace.resolve('src'); // const appPackageJsonPath = opts.workspace.resolve('package.json') const appNodeModulesPath = opts.workspace.resolve('node_modules'); const pkgJson = options.workspace.packageJson; const appIndexHtmlPath = getIndexHtmlPath(opts.workspace); const useTypeScript = fs_1.default.existsSync(appTsConfigPath); const shouldUseRelativeAssetPaths = options.publicPath === './'; const shouldUploadSourcemaps = opts.env === 'production' ? isEnvValueTrue(process.env.BUGSNAG_UPLOAD_SOURCEMAPS) || false : false; const suffix = Buffer.from(pkgJson.name + pkgJson.version) .toString('base64') .slice(0, 5); const envVariables = (0, environment_1.getEnvVariables)({ WDS_SOCKET_HOST: (_a = options.hmr) === null || _a === void 0 ? void 0 : _a.host, WDS_SOCKET_PORT: (_b = options.hmr) === null || _b === void 0 ? void 0 : _b.port, WDS_SOCKET_PATH: (_c = options.hmr) === null || _c === void 0 ? void 0 : _c.socketPath, NODE_ENV: opts.env, PUBLIC_URL: (0, exports.normalizePublicPath)(options.publicUrl), RAILS_MODE: opts.railsMode, }); const hasModuleFederation = ((_d = options.workspace.procoreConfig.app) === null || _d === void 0 ? void 0 : _d.moduleFederation) !== undefined; function injectStyleLoaders(rule, cssOptions, preProcessor) { rule.when(isEnvDevelopment, (rule) => { rule .use('style') .loader(require.resolve('style-loader')) .options({ esModule: false }); }, (rule) => { rule .use('cssExtract') .loader(mini_css_extract_plugin_1.default.loader) .options(shouldUseRelativeAssetPaths ? { esModule: false, publicPath: '../../' } : { esModule: false }); }); rule .use('css') .loader(require.resolve('css-loader')) .options({ ...cssOptions, esModule: false }); rule .use('postcss') .loader(require.resolve('postcss-loader')) .options({ postcssOptions: { ident: 'postcss', plugins: [ postcss_flexbugs_fixes_1.default, (0, postcss_preset_env_1.default)({ autoprefixer: { flexbox: 'no-2009', }, browsers: BROWSER_LIST, stage: 3, }), (0, postcss_normalize_1.default)(), ], }, sourceMap: isEnvProduction && options.shouldUseSourceMap, }); rule.when(preProcessor, (rule) => { rule .use('resolveUrl') .loader(require.resolve('resolve-url-loader')) .options({ sourceMap: isEnvProduction && options.shouldUseSourceMap, }); rule.use(preProcessor).loader(require.resolve(preProcessor)).options({ sourceMap: true, }); }); } config.performance .hints(false) .maxEntrypointSize(Infinity) .maxAssetSize(Infinity); config .mode(options.env) .bail(isEnvProduction) .context(opts.workspace.context) .when(isEnvProduction && options.shouldUseSourceMap, (config) => config.devtool('source-map'), (config) => config.devtool('cheap-module-source-map')); Object.entries(options.entries).forEach((entry) => { const entryName = `${entry[0]}.bundle`; const entryPath = opts.workspace.resolve('src', entry[1]); config .entry(entryName) .when(isEnvDevelopment, (entry) => entry.add(require.resolve('react-dev-utils/webpackHotDevClient'))) .add(entryPath); }); // webpack5 drops built in polyfills, add them back in config.merge({ target: ['web', 'es5'], resolve: { fallback: { assert: 'assert', buffer: 'buffer', console: 'console-browserify', constants: 'constants-browserify', crypto: 'crypto-browserify', domain: 'domain-browser', events: 'events', http: 'stream-http', https: 'https-browserify', os: 'os-browserify/browser', path: 'path-browserify', punycode: 'punycode', process: 'process/browser', querystring: 'querystring-es3', stream: 'stream-browserify', _stream_duplex: 'readable-stream/duplex', _stream_passthrough: 'readable-stream/passthrough', _stream_readable: 'readable-stream/readable', _stream_transform: 'readable-stream/transform', _stream_writable: 'readable-stream/writable', string_decoder: 'string_decoder', sys: 'util', timers: 'timers-browserify', tty: 'tty-browserify', url: 'url', util: 'util', vm: 'vm-browserify', zlib: 'browserify-zlib', }, }, }); config.output.merge({ // TODO: Use `static/js/bundle.js` for filename chunkFilename: 'static/js/[name].chunk.js', crossOriginLoading: false, devtoolModuleFilenameTemplate: (info) => path_1.default.resolve(info.absoluteResourcePath).replace(/\\/g, '/'), filename: '[name].js', globalObject: 'self', hashFunction: 'xxhash64', path: '/', pathinfo: isEnvDevelopment, publicPath: (0, exports.normalizePublicPath)(options.publicPath), }); config.output.when(isEnvProduction, (output) => { output.merge({ path: appBuildPath, chunkFilename: 'static/js/[name].[contenthash:8].chunk.js', filename: 'static/js/[name].[contenthash:8].js', devtoolModuleFilenameTemplate: (info) => path_1.default .relative(appSrcPath, info.absoluteResourcePath) .replace(/\\/g, '/'), }); }); config.optimization.minimize(isEnvProduction).runtimeChunk(false); config.optimization.minimizer('terser').use(terser_webpack_plugin_1.default, [ { terserOptions: { parse: { ecma: 8, }, compress: { ecma: 5, warnings: false, comparisons: false, inline: 2, }, // TODO: investigate this config // mangle: { // safari10: true, // }, mangle: true, keep_classnames: options.profile, // TODO: investigate this config // keep_fnames: options.profile, keep_fnames: options.profile ? options.profile : /./, output: { ecma: 5, comments: false, ascii_only: true, }, }, parallel: true, }, ]); config.optimization.minimizer('optimizeCSS').use(css_minimizer_webpack_plugin_1.default); config.resolve.modules.add('node_modules').add(appNodeModulesPath); config.resolve.alias .set('@', appSrcPath) .set('react-native', 'react-native-web') .when(options.profile, (alias) => alias.merge({ 'react-dom$': 'react-dom/profiling', 'scheduler/tracing': 'scheduler/tracing-profiling', })); ['cjs', 'mjs', 'js', 'ts', 'tsx', 'json', 'jsx'] .map((ext) => `.${ext}`) .map((ext) => config.resolve.extensions.add(ext)); // config.resolve // .plugin('moduleScope') // .use(ModuleScopePlugin, [appSrcPath, [appPackageJsonPath]]) config.module .rule('requireEnsure') .test(/\.[cm]?js$/) .parser({ requireEnsure: false }); config.module .rule('compiler') .oneOf('url') .test([/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/]) .use('url') .loader(require.resolve('url-loader')) .options({ limit: 10000, name: 'static/media/[name].[hash:8].[ext]', }); config.module .rule('compiler') .oneOf('worker') .test(/\.worker\.(js|jsx|ts|tsx)$/) .include.add(appSrcPath) .end() .use('worker') .options({ inline: 'fallback' }) .loader(require.resolve('worker-loader')) .end() .use('babel') .loader(require.resolve('babel-loader')) .options((0, config_1.createConfigForReact)({ env: opts.env })); config.module .rule('compiler') .oneOf('babel') .test(/\.(js|cjs|mjs|jsx|ts|tsx)$/) .include.add(appSrcPath) .end() .exclude.add(appNodeModulesPath) .end() .use('babel') .loader(require.resolve('babel-loader')) .options((0, config_1.createConfigForReact)({ env: opts.env })); config.module .rule('compiler') .oneOf('babelDeps') .test(/\.(js|cjs|mjs)$/) .resolve.set('fullySpecified', false) .end() .exclude.add(/@babel(?:\/|\\{1,2})runtime/) .add(/core-js/) .add(appSrcPath) .end() .use('babel') .loader(require.resolve('babel-loader')) .options((0, config_1.createConfigForDependencies)({ env: opts.env, shouldUseSourceMap: options.shouldUseSourceMap, })); config.module .rule('compiler') .oneOf('babepDeps') .test(/\.(js|cjs|mjs)$/) .include.add(/@babel(?:\/|\\{1,2})runtime/) .end() .resolve.set('fullySpecified', false); config.module .rule('compiler') .oneOf('cssModules') .test(/\.css$/); injectStyleLoaders(config.module.rule('compiler').oneOf('cssModules'), { importLoaders: 1, sourceMap: isEnvProduction && options.shouldUseSourceMap, modules: { getLocalIdent: getCSSModuleLocalIdent_1.default, }, }); config.module .rule('compiler') .oneOf('sassModules') .test(/\.(scss|sass)$/) .exclude.add(/core-css(.*)\.scss/) .end(); injectStyleLoaders(config.module.rule('compiler').oneOf('sassModules'), { importLoaders: 2, sourceMap: isEnvProduction && options.shouldUseSourceMap, modules: { getLocalIdent: getCSSModuleLocalIdent_1.default, }, }, 'sass-loader'); config.module .rule('compiler') .oneOf('coreCssModules') .test(/core-css(.*)\.scss$/) .end(); injectStyleLoaders(config.module.rule('compiler').oneOf('coreCssModules'), { importLoaders: 2, sourceMap: isEnvProduction && options.shouldUseSourceMap, modules: { getLocalIdent: (...args) => { return `${(0, getCSSModuleLocalIdent_1.default)(...args)}_${suffix}`; }, }, }, 'sass-loader'); config.module .rule('compiler') .oneOf('file') .exclude.add(/\.(js|cjs|mjs|jsx|ts|tsx|html|json)$/) .end() .use('file') .loader(require.resolve('file-loader')) .options({ name: 'static/media/[name].[hash:8].[ext]', }); config.plugin('html').use(html_webpack_plugin_1.default, [ { inject: true, template: appIndexHtmlPath, minify: isEnvProduction ? { removeComments: true, collapseWhitespace: true, removeRedundantAttributes: true, useShortDoctype: true, removeEmptyAttributes: true, removeStyleLinkTypeAttributes: true, keepClosingSlash: true, minifyJS: true, minifyCSS: true, minifyURLs: true, } : false, }, ]); config.when(isEnvProduction && options.shouldInlineRuntimeChunk, () => { config .plugin('inlineRuntimeChunk') .use(InlineChunkHtmlPlugin_1.default, [html_webpack_plugin_1.default, [/runtime-.+[.]js/]]); }); config .plugin('interpolateHtml') .use(InterpolateHtmlPlugin_1.default, [html_webpack_plugin_1.default, envVariables]); config .plugin('moduleNotFound') .use(ModuleNotFoundPlugin_1.default, [opts.workspace.context]); config .plugin('injectEnvVariables') .use(webpack_1.default.DefinePlugin, [(0, environment_1.stringifyEnvironment)(envVariables)]); config.when(isEnvDevelopment, (config) => { config .plugin('hotModuleReplacement') .use(webpack_1.default.HotModuleReplacementPlugin); }); config.when(isEnvDevelopment, (config) => { config.plugin('caseSensitivePaths').use(case_sensitive_paths_webpack_plugin_1.default); }); config.when(isEnvProduction, (config) => { config.plugin('extractCss').use(mini_css_extract_plugin_1.default, [ { filename: 'static/css/[name].[contenthash:8].css', chunkFilename: 'static/css/[name].[contenthash:8].chunk.css', }, ]); }); config.when(shouldUploadSourcemaps, (config) => { config.plugin('bugsnag').use(webpack_bugsnag_plugins_1.BugsnagSourceMapUploaderPlugin, [ { apiKey: envVariables.PROCORE_HYDRA_BUGSNAG_API_KEY, publicPath: process.env.BUGSNAG_SOURCEMAP_URL || 'https://assets*.procore.com', overwrite: true, }, ]); }); config.when(Boolean(opts.analyze), () => { config.plugin('bundleAnalyzer').use(webpack_bundle_analyzer_1.BundleAnalyzerPlugin); }); config.plugin('minifest').use(webpack_manifest_plugin_1.WebpackManifestPlugin, [ { fileName: 'asset-manifest.json', publicPath: options.publicPath, generate: (seed, files, // eslint-disable-next-line @typescript-eslint/no-unused-vars _entrypoints) => { function addToManifest(manifestSeed, file) { return { ...manifestSeed, [file.name]: resolveCdn(file.path), }; } const manifestFiles = files.reduce(addToManifest, seed); // const entrypointFiles = entrypoints.main.filter( // (fileName) => !fileName.endsWith('.map') // ); // TODO: break change once Hydra gem is fixed. // return { // files: manifestFiles, // entrypoints: entrypointFiles, // }; return manifestFiles; }, }, ]); config.plugin('ignore').use(webpack_1.default.IgnorePlugin, [ { resourceRegExp: /^\.\/locale$/, contextRegExp: /moment$/, }, ]); config.when(isEnvProduction, (config) => { config.plugin('serviceWorker').use(workbox_webpack_plugin_1.default.GenerateSW, [ { clientsClaim: true, exclude: [/\.map$/, /asset-manifest\.json$/], navigateFallback: `${options.publicUrl}/index.html`, navigateFallbackDenylist: [ // eslint-disable-next-line prefer-regex-literals new RegExp('^/_'), // eslint-disable-next-line prefer-regex-literals new RegExp('/[^/?]+\\.[^/]+$'), ], }, ]); }); config.when(useTypeScript, (config) => { config.plugin('typescriptCheck').use(ForkTsCheckerWebpackPlugin_1.default, [ { async: isEnvDevelopment, typescript: { typescriptPath: resolve_1.default.sync('typescript', { basedir: appNodeModulesPath, }), configOverride: { compilerOptions: { sourceMap: isEnvProduction ? options.shouldUseSourceMap : isEnvDevelopment, skipLibCheck: true, inlineSourceMap: false, declarationMap: false, noEmit: true, incremental: true, tsBuildInfoFile: opts.workspace.resolve('node_modules', '.cache', 'tsconfig.tsbuildinfo'), }, }, context: opts.workspace.context, diagnosticOptions: { syntactic: true, }, mode: 'write-references', // profile: true, }, issue: { include: [ { file: '../**/src/**/*.{ts,tsx}' }, { file: '**/src/**/*.{ts,tsx}' }, ], exclude: [ { file: '**/src/**/__tests__/**' }, { file: '**/src/**/?(*.){spec|test}.*' }, { file: '**/src/setupProxy.*' }, { file: '**/src/setupTests.*' }, ], }, logger: { infrastructure: 'silent', }, }, ]); }); config.when(hasModuleFederation, (config) => { (0, moduleFederationDecorator_1.setupModuleFederation)(config, options); }); return config; } //# sourceMappingURL=setupWebpack.js.map