From f480ff7540e19a471c739542e80697a58cbcf7e9 Mon Sep 17 00:00:00 2001 From: Vasyl Ilba Date: Tue, 17 Oct 2023 00:39:42 +0300 Subject: [PATCH 01/14] new build parallel process with worker farm (faster for big amounts); new optional flag -p --- cli/displayDevServer.js | 1 + package-lock.json | 64 +++++++++++++++++++++++++++---- package.json | 3 +- src/index.js | 10 +++-- src/webpack/buildFilesParallel.js | 43 +++++++++++++++++++++ src/webpack/webpackRun.js | 37 ++++++++++++++++++ 6 files changed, 147 insertions(+), 11 deletions(-) create mode 100644 src/webpack/buildFilesParallel.js create mode 100644 src/webpack/webpackRun.js diff --git a/cli/displayDevServer.js b/cli/displayDevServer.js index b3cee14..75383c9 100755 --- a/cli/displayDevServer.js +++ b/cli/displayDevServer.js @@ -18,6 +18,7 @@ program .option('-o, --outputDir ', 'output dir', './build') .option('--skipBuild', 'skip compiling ads phase', false) .option('--skipPreview', 'skip preview building phase', false) + .option('-p, --parallel [data]', 'run webpack in parallel') .parse(process.argv); const options = program.opts(); diff --git a/package-lock.json b/package-lock.json index c416dc0..a6eaec0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52,7 +52,8 @@ "webpack": "^5.75.0", "webpack-dev-middleware": "^6.0.1", "webpack-hot-middleware": "^2.25.3", - "webpack-virtual-modules": "^0.5.0" + "webpack-virtual-modules": "^0.5.0", + "worker-farm": "^1.7.0" }, "bin": { "dds": "cli/displayDevServer.js" @@ -3048,9 +3049,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001458", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001458.tgz", - "integrity": "sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w==", + "version": "1.0.30001549", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001549.tgz", + "integrity": "sha512-qRp48dPYSCYaP+KurZLhDYdVE+yEyht/3NlmcJgVQ2VMGt6JL36ndQ/7rgspdZsJuxDPFIo/OzBT2+GmIJ53BA==", "funding": [ { "type": "opencollective", @@ -3059,6 +3060,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -3704,6 +3709,17 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -6386,6 +6402,11 @@ "node": ">= 0.10" } }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==" + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -7630,6 +7651,14 @@ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" }, + "node_modules/worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dependencies": { + "errno": "~0.1.7" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -9702,9 +9731,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001458", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001458.tgz", - "integrity": "sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w==" + "version": "1.0.30001549", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001549.tgz", + "integrity": "sha512-qRp48dPYSCYaP+KurZLhDYdVE+yEyht/3NlmcJgVQ2VMGt6JL36ndQ/7rgspdZsJuxDPFIo/OzBT2+GmIJ53BA==" }, "chalk": { "version": "4.1.2", @@ -10155,6 +10184,14 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==" }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "requires": { + "prr": "~1.0.1" + } + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -12016,6 +12053,11 @@ "ipaddr.js": "1.9.1" } }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==" + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -12920,6 +12962,14 @@ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "requires": { + "errno": "~0.1.7" + } + }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index f8cb4b4..e1280f0 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "webpack": "^5.75.0", "webpack-dev-middleware": "^6.0.1", "webpack-hot-middleware": "^2.25.3", - "webpack-virtual-modules": "^0.5.0" + "webpack-virtual-modules": "^0.5.0", + "worker-farm": "^1.7.0" } } diff --git a/src/index.js b/src/index.js index 3d0eae8..81c4da4 100644 --- a/src/index.js +++ b/src/index.js @@ -1,18 +1,22 @@ const getWebpackConfigs = require("./webpack/getWebpackConfigs"); const devServer = require("./webpack/devServer"); const buildFiles = require("./webpack/buildFiles"); +const buildFilesParallel = require("./webpack/buildFilesParallel"); const buildPreview = require("./webpack/buildPreview"); module.exports = async function (options) { // {mode = "development", glob = "./**/.richmediarc*", choices = null, stats = null, outputDir = "./build", configOverride = {}} - let {mode, glob, choices, stats, outputDir, skipBuild, skipPreview} = options; + let {mode, glob, choices, stats, outputDir, skipBuild, skipPreview, parallel} = options; const webpackConfigs = !skipBuild ? await getWebpackConfigs(options) : null; if (mode === "development") { - await devServer(webpackConfigs.result, webpackConfigs.choices.openLocation); + await devServer(webpackConfigs.result, webpackConfigs.choices.openLocation); } else { - if (!skipBuild) await buildFiles(webpackConfigs.result, outputDir); + if (!skipBuild) { + if (parallel) await buildFilesParallel(webpackConfigs.result, options); + else await buildFiles(webpackConfigs.result, outputDir); + } if (!skipPreview) await buildPreview(outputDir); } }; diff --git a/src/webpack/buildFilesParallel.js b/src/webpack/buildFilesParallel.js new file mode 100644 index 0000000..32d526c --- /dev/null +++ b/src/webpack/buildFilesParallel.js @@ -0,0 +1,43 @@ +const path = require("path"); +const removeTempRichmediaRc = require("../util/removeTempRichmediaRc"); + +const cliProgress = require("cli-progress"); + +const workerFarm = require('worker-farm'); + +module.exports = async function buildFiles(result, options) { + const webpackRun = workerFarm( + { + autoStart: true, + maxRetries: 0, + maxConcurrentCallsPerWorker: 1, + maxConcurrentWorkers: options.parallel === true ? 4 : options.parallel + }, + require.resolve('./webpackRun') + ); + + const startTime = new Date().getTime(); + const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic); + progressBar.start(result.length, 0); + + await Promise.all(result.map((result) => { + delete result.settings.row + return new Promise(res => webpackRun({ config: result.settings, options }, () => { + progressBar.increment() + res() + })); + })); + + workerFarm.end(webpackRun) + + progressBar.stop(); + console.log(`built in ${new Date().getTime() - startTime}ms`); + + // final clean up + console.log("Removing temp .richmediarc..."); + removeTempRichmediaRc(result); + + return { + outputDir: path.resolve(options.outputDir), + }; +}; diff --git a/src/webpack/webpackRun.js b/src/webpack/webpackRun.js new file mode 100644 index 0000000..703fe86 --- /dev/null +++ b/src/webpack/webpackRun.js @@ -0,0 +1,37 @@ +const webpack = require('webpack') +const chalk = require('chalk') + +const createConfigByRichmediarcList = require('./config/createConfigByRichmediarcList') + +module.exports = async function ({ config, options }, cb) { + + const webpackConfig = await createConfigByRichmediarcList([config], options); + + webpack(webpackConfig[0]).run((err, stats) => { + if (err) { + console.error(err.stack || err); + if (err.details) { + err.details.forEach((item, index) => { + console.error(index, item.message); + }); + } + return; + } + + const info = stats.toJson(); + + if (stats.hasErrors()) { + info.errors.forEach((item, index) => { + console.log(chalk.red(item.message)); + }); + } + + if (stats.hasWarnings()) { + info.warnings.forEach((item) => { + console.log(chalk.green(item.message)); + }); + } + + cb(); + }); +} From 0aa87d9cf82fecc955cf0ce92e48b5a2ed868896 Mon Sep 17 00:00:00 2001 From: Vasyl Ilba Date: Tue, 17 Oct 2023 01:05:14 +0300 Subject: [PATCH 02/14] new dev parallel process with worker farm (faster for big amounts); devtools don't work because of security and CORS --- src/index.js | 4 +- src/webpack/devServerParallel.js | 180 +++++++++++++++++++++++++++++++ src/webpack/devSubServer.js | 51 +++++++++ 3 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 src/webpack/devServerParallel.js create mode 100644 src/webpack/devSubServer.js diff --git a/src/index.js b/src/index.js index 81c4da4..c13e74c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ const getWebpackConfigs = require("./webpack/getWebpackConfigs"); const devServer = require("./webpack/devServer"); +const devServerParallel = require("./webpack/devServerParallel"); const buildFiles = require("./webpack/buildFiles"); const buildFilesParallel = require("./webpack/buildFilesParallel"); const buildPreview = require("./webpack/buildPreview"); @@ -11,7 +12,8 @@ module.exports = async function (options) { const webpackConfigs = !skipBuild ? await getWebpackConfigs(options) : null; if (mode === "development") { - await devServer(webpackConfigs.result, webpackConfigs.choices.openLocation); + if (parallel) await devServerParallel(webpackConfigs.result, webpackConfigs.choices.openLocation, options); + else await devServer(webpackConfigs.result, webpackConfigs.choices.openLocation); } else { if (!skipBuild) { if (parallel) await buildFilesParallel(webpackConfigs.result, options); diff --git a/src/webpack/devServerParallel.js b/src/webpack/devServerParallel.js new file mode 100644 index 0000000..af74bb8 --- /dev/null +++ b/src/webpack/devServerParallel.js @@ -0,0 +1,180 @@ +const path = require('path'); +const fs = require('fs-extra'); +const express = require('express'); +const portfinder = require('portfinder'); +const util = require('util'); +const chalk = require('chalk'); +const open = require('open'); + +const extendObject = require('../util/extendObject'); +const createObjectFromJSONPath = require('../util/createObjectFromJSONPath'); +const getDataFromGoogleSpreadsheet = require('../util/getDataFromGoogleSpreadsheet'); +const removeTempRichmediaRc = require('../util/removeTempRichmediaRc'); + +const getNameFromLocation = require('../util/getNameFromLocation'); + +const workerFarm = require('worker-farm'); + +/** + * + * @param {Array<{webpack: *, settings: {location, data}}>} configs + * @param {boolean} openLocation + * @param {{}} options + */ +module.exports = async function devServer(configs, openLocation = true, options) { + + const N_SUBSERVERS = options.parallel === true ? 4 : options.parallel + + const settingsList = configs.map(({ settings }) => settings); + const port = await portfinder.getPortPromise(); + + const devSubServer = workerFarm( + { + maxRetries: 0, + autoStart: true, + maxConcurrentCallsPerWorker: 1, + maxConcurrentWorkers: N_SUBSERVERS + }, + require.resolve('./devSubServer') + ); + + const httpLocation = `http://localhost:${port}`; + + console.log(`${chalk.blue('i')} Server running. Please go to ${httpLocation} +${chalk.grey.bold('-------------------------------------------------------')} +`); + + const app = express(); + + app.listen(port, () => {}); + + const ports = await new Promise(res => portfinder.getPorts(N_SUBSERVERS, {}, (err, ports) => res(ports))) + + await Promise.all(settingsList + // spread work evenly into chunks + .reduce((acc, v, i) => { + acc[i % N_SUBSERVERS].push(v) + return acc + }, Array.from({length: N_SUBSERVERS}, () => [])) + // if we have empty chunks (less than N_SUBSERVERS banners) + .filter(e => e.length) + // map to promises + .map(async (chunk, i) => { + // delete row since it's an object with constructor and we can't carry it to the thread + chunk = chunk.map(({row, ...config}) => config) + + const port = ports[i] + + chunk.forEach(config => { + const name = getNameFromLocation(config.location); + app.use(`/${name}/`, (req, res, next) => { + res.redirect(`http://localhost:${port}/${name}/`) + }) + }) + + return new Promise(res => devSubServer({configs: chunk, options, port}, res)) + })); + + app.use('/', express.static(path.join(__dirname, '../preview/dist'))); + + openLocation && open(httpLocation); + + app.get('/data/ads.json', (req, res) => { + res.json({ + isGoogleSpreadsheetBanner: typeof configs[0].settings.data.settings.contentSource !== 'undefined', + ads: settingsList.map(e => { + const assetName = getNameFromLocation(e.location) + const bundleName = e.data.settings.bundleName || getNameFromLocation(e.location) + const url = `${httpLocation}/${assetName}/index.html` + return { + url, + ...e.data.settings.size, + bundleName, + output: { + html: { + url, + }, + }, + } + }) + }) + }) + + app.get("/reload_dynamic_data", async function (req, res) { + const contentSource = configs[0].settings.data.settings.contentSource; + const spreadsheetData = await getDataFromGoogleSpreadsheet(contentSource); + + configs.forEach(config => { + let row = spreadsheetData.rows[config.settings.row.rowNumber-2]; //for example, row number 2 is array element 0 + + const staticRow = spreadsheetData.headerValues.reduce((prev, name) => { + prev[name] = row[name]; + return prev; + }, {}); + + let staticRowObject = {}; + for (const key in staticRow) { + if (staticRow.hasOwnProperty(key)) { + let obj = createObjectFromJSONPath(key, staticRow[key]); + extendObject(staticRowObject, obj); + } + } + + // filter out everything that is not needed. + if (config.settings.data.settings.contentSource.filter) { + const filters = []; + if (config.settings.data.settings.contentSource.filter instanceof Array) { + filters.push(...config.settings.data.settings.contentSource.filter); + } else { + filters.push(config.settings.data.settings.contentSource.filter); + } + + // for loop so i can break or return emmediatly + for (let j = 0; j < filters.length; j++) { + const filter = filters[j]; + for (const key in filter) { + if (filter.hasOwnProperty(key) && staticRowObject[key] && staticRowObject[key] !== filter[key]) { + return; + } + } + } + } + + // new content object with updated content from sheet + let content = extendObject({}, (config.settings.data.content || {}), staticRowObject) + + // next 4 lines is reading existing richmediarc from the disk, updating the content object, and then writing the new file to disk again + const configFile = fs.readFileSync(config.settings.location, {encoding:'utf8', flag:'r'}) + const configFileJson = JSON.parse(configFile); + + if (!util.isDeepStrictEqual(configFileJson.content, content)) { //compare 'new' content with old content. If anything has changed, write a new file + configFileJson.content = content; + fs.writeFileSync(config.settings.location, Buffer.from(JSON.stringify(configFileJson))); + } + }) + + res.send('ok'); + }); + + // eslint-disable-next-line + process.stdin.resume(); //so the program will not close instantly + + function exitHandler(options, exitCode) { + if (options.cleanup) removeTempRichmediaRc(configs); + if (exitCode || exitCode === 0) console.log(exitCode); + if (options.exit) process.exit(); + } + + //do something when app is closing + process.on('exit', exitHandler.bind(null,{cleanup:true})); + + //catches ctrl+c event + process.on('SIGINT', exitHandler.bind(null, {exit:true})); + + // catches "kill pid" (for example: nodemon restart) + process.on('SIGUSR1', exitHandler.bind(null, {exit:true})); + process.on('SIGUSR2', exitHandler.bind(null, {exit:true})); + + //catches uncaught exceptions + process.on('uncaughtException', exitHandler.bind(null, {exit:true})); +}; diff --git a/src/webpack/devSubServer.js b/src/webpack/devSubServer.js new file mode 100644 index 0000000..bd1d525 --- /dev/null +++ b/src/webpack/devSubServer.js @@ -0,0 +1,51 @@ +const webpack = require('webpack'); +const webpackHotMiddleware = require('webpack-hot-middleware'); +const webpackDevMiddleware = require('webpack-dev-middleware'); +const express = require('express'); + +const createConfigByRichmediarcList = require('./config/createConfigByRichmediarcList') + +const getNameFromLocation = require('../util/getNameFromLocation'); + +/** + * + * @param {Array<{settings: {location, data}}>} configs + * @param {{}} options + * @param {number} port + */ +module.exports = async function devSubServer({configs, options, port}, cb) { + const webpackConfigList = await createConfigByRichmediarcList(configs, options); + const settingsList = configs; + + const app = express(); + + webpackConfigList.forEach((config, index) => { + const hmrPath = '__webpack_hmr'; + const name = getNameFromLocation(settingsList[index].location); + + config.mode = 'development'; + + config.output = { + ...config.output, + hotUpdateChunkFilename: '.hot/.hot-update.js', + hotUpdateMainFilename: '.hot/.hot-update.json', + }; + + const compiler = webpack(config, () => {}); + + app.use( + webpackDevMiddleware(compiler, { + publicPath: `/${name}/`, + }), + ); + + app.use( + webpackHotMiddleware(compiler, { + path: `/${name}/${hmrPath}`, + }), + ); + }); + + app.listen(port, () => {}); + cb(); +}; From 7340734b13503db060e3dc2395acd446e0db5065 Mon Sep 17 00:00:00 2001 From: Vasyl Ilba Date: Tue, 17 Oct 2023 12:27:49 +0300 Subject: [PATCH 03/14] fixes dynamic data comparison for dev --- src/webpack/devServer.js | 1 + src/webpack/devServerParallel.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/webpack/devServer.js b/src/webpack/devServer.js index dcea8f3..8bdd9cc 100644 --- a/src/webpack/devServer.js +++ b/src/webpack/devServer.js @@ -138,6 +138,7 @@ ${chalk.grey.bold('-------------------------------------------------------')} // next 4 lines is reading existing richmediarc from the disk, updating the content object, and then writing the new file to disk again const configFile = fs.readFileSync(config.settings.location, {encoding:'utf8', flag:'r'}) const configFileJson = JSON.parse(configFile); + content = JSON.parse(JSON.stringify(content)) if (!util.isDeepStrictEqual(configFileJson.content, content)) { //compare 'new' content with old content. If anything has changed, write a new file configFileJson.content = content; diff --git a/src/webpack/devServerParallel.js b/src/webpack/devServerParallel.js index af74bb8..61c909b 100644 --- a/src/webpack/devServerParallel.js +++ b/src/webpack/devServerParallel.js @@ -146,6 +146,7 @@ ${chalk.grey.bold('-------------------------------------------------------')} // next 4 lines is reading existing richmediarc from the disk, updating the content object, and then writing the new file to disk again const configFile = fs.readFileSync(config.settings.location, {encoding:'utf8', flag:'r'}) const configFileJson = JSON.parse(configFile); + content = JSON.parse(JSON.stringify(content)) if (!util.isDeepStrictEqual(configFileJson.content, content)) { //compare 'new' content with old content. If anything has changed, write a new file configFileJson.content = content; From 9fb1a9616d76a6d07813f520ab201b5e2ab6f71e Mon Sep 17 00:00:00 2001 From: Vasyl Ilba Date: Wed, 18 Oct 2023 23:59:37 +0300 Subject: [PATCH 04/14] waiting for webpack to dev all configs before opening the browser --- src/webpack/devServerParallel.js | 2 +- src/webpack/devSubServer.js | 32 +++++++++++++++++--------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/webpack/devServerParallel.js b/src/webpack/devServerParallel.js index 61c909b..bdfeb5c 100644 --- a/src/webpack/devServerParallel.js +++ b/src/webpack/devServerParallel.js @@ -59,7 +59,7 @@ ${chalk.grey.bold('-------------------------------------------------------')} // if we have empty chunks (less than N_SUBSERVERS banners) .filter(e => e.length) // map to promises - .map(async (chunk, i) => { + .map((chunk, i) => { // delete row since it's an object with constructor and we can't carry it to the thread chunk = chunk.map(({row, ...config}) => config) diff --git a/src/webpack/devSubServer.js b/src/webpack/devSubServer.js index bd1d525..c7eb02e 100644 --- a/src/webpack/devSubServer.js +++ b/src/webpack/devSubServer.js @@ -19,7 +19,7 @@ module.exports = async function devSubServer({configs, options, port}, cb) { const app = express(); - webpackConfigList.forEach((config, index) => { + await Promise.all(webpackConfigList.map((config, index) => { const hmrPath = '__webpack_hmr'; const name = getNameFromLocation(settingsList[index].location); @@ -31,20 +31,22 @@ module.exports = async function devSubServer({configs, options, port}, cb) { hotUpdateMainFilename: '.hot/.hot-update.json', }; - const compiler = webpack(config, () => {}); - - app.use( - webpackDevMiddleware(compiler, { - publicPath: `/${name}/`, - }), - ); - - app.use( - webpackHotMiddleware(compiler, { - path: `/${name}/${hmrPath}`, - }), - ); - }); + return new Promise(res => { + const compiler = webpack(config, res); + + app.use( + webpackDevMiddleware(compiler, { + publicPath: `/${name}/`, + }), + ); + + app.use( + webpackHotMiddleware(compiler, { + path: `/${name}/${hmrPath}`, + }), + ); + }) + })); app.listen(port, () => {}); cb(); From deff935592fce7125c1e523fd929b022ca53dab2 Mon Sep 17 00:00:00 2001 From: Vasyl Ilba Date: Tue, 17 Oct 2023 00:39:42 +0300 Subject: [PATCH 05/14] new build parallel process with worker farm (faster for big amounts); new optional flag -p --- cli/displayDevServer.js | 1 + package-lock.json | 64 +++++++++++++++++++++++++++---- package.json | 3 +- src/index.js | 12 ++++-- src/webpack/buildFilesParallel.js | 43 +++++++++++++++++++++ src/webpack/webpackRun.js | 37 ++++++++++++++++++ 6 files changed, 148 insertions(+), 12 deletions(-) create mode 100644 src/webpack/buildFilesParallel.js create mode 100644 src/webpack/webpackRun.js diff --git a/cli/displayDevServer.js b/cli/displayDevServer.js index b3cee14..75383c9 100755 --- a/cli/displayDevServer.js +++ b/cli/displayDevServer.js @@ -18,6 +18,7 @@ program .option('-o, --outputDir ', 'output dir', './build') .option('--skipBuild', 'skip compiling ads phase', false) .option('--skipPreview', 'skip preview building phase', false) + .option('-p, --parallel [data]', 'run webpack in parallel') .parse(process.argv); const options = program.opts(); diff --git a/package-lock.json b/package-lock.json index 111df93..2e6990c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52,7 +52,8 @@ "webpack": "^5.75.0", "webpack-dev-middleware": "^6.0.1", "webpack-hot-middleware": "^2.25.3", - "webpack-virtual-modules": "^0.5.0" + "webpack-virtual-modules": "^0.5.0", + "worker-farm": "^1.7.0" }, "bin": { "dds": "cli/displayDevServer.js" @@ -3048,9 +3049,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001458", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001458.tgz", - "integrity": "sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w==", + "version": "1.0.30001549", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001549.tgz", + "integrity": "sha512-qRp48dPYSCYaP+KurZLhDYdVE+yEyht/3NlmcJgVQ2VMGt6JL36ndQ/7rgspdZsJuxDPFIo/OzBT2+GmIJ53BA==", "funding": [ { "type": "opencollective", @@ -3059,6 +3060,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -3704,6 +3709,17 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -6386,6 +6402,11 @@ "node": ">= 0.10" } }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==" + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -7630,6 +7651,14 @@ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" }, + "node_modules/worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dependencies": { + "errno": "~0.1.7" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -9702,9 +9731,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001458", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001458.tgz", - "integrity": "sha512-lQ1VlUUq5q9ro9X+5gOEyH7i3vm+AYVT1WDCVB69XOZ17KZRhnZ9J0Sqz7wTHQaLBJccNCHq8/Ww5LlOIZbB0w==" + "version": "1.0.30001549", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001549.tgz", + "integrity": "sha512-qRp48dPYSCYaP+KurZLhDYdVE+yEyht/3NlmcJgVQ2VMGt6JL36ndQ/7rgspdZsJuxDPFIo/OzBT2+GmIJ53BA==" }, "chalk": { "version": "4.1.2", @@ -10155,6 +10184,14 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==" }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "requires": { + "prr": "~1.0.1" + } + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -12016,6 +12053,11 @@ "ipaddr.js": "1.9.1" } }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==" + }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -12920,6 +12962,14 @@ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "requires": { + "errno": "~0.1.7" + } + }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index ba3fba8..d84db0d 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "webpack": "^5.75.0", "webpack-dev-middleware": "^6.0.1", "webpack-hot-middleware": "^2.25.3", - "webpack-virtual-modules": "^0.5.0" + "webpack-virtual-modules": "^0.5.0", + "worker-farm": "^1.7.0" } } diff --git a/src/index.js b/src/index.js index 1b68597..81c4da4 100644 --- a/src/index.js +++ b/src/index.js @@ -1,18 +1,22 @@ const getWebpackConfigs = require("./webpack/getWebpackConfigs"); const devServer = require("./webpack/devServer"); const buildFiles = require("./webpack/buildFiles"); +const buildFilesParallel = require("./webpack/buildFilesParallel"); const buildPreview = require("./webpack/buildPreview"); module.exports = async function (options) { // {mode = "development", glob = "./**/.richmediarc*", choices = null, stats = null, outputDir = "./build", configOverride = {}} - let {mode, glob, choices, stats, outputDir, skipBuild, skipPreview} = options; + let {mode, glob, choices, stats, outputDir, skipBuild, skipPreview, parallel} = options; const webpackConfigs = !skipBuild ? await getWebpackConfigs(options) : null; if (mode === "development") { - await devServer(webpackConfigs.result, webpackConfigs.choices.openLocation); + await devServer(webpackConfigs.result, webpackConfigs.choices.openLocation); } else { - if (!skipBuild) await buildFiles(webpackConfigs.result, outputDir); - if (!skipPreview) await buildPreview(webpackConfigs?.result, outputDir); + if (!skipBuild) { + if (parallel) await buildFilesParallel(webpackConfigs.result, options); + else await buildFiles(webpackConfigs.result, outputDir); + } + if (!skipPreview) await buildPreview(outputDir); } }; diff --git a/src/webpack/buildFilesParallel.js b/src/webpack/buildFilesParallel.js new file mode 100644 index 0000000..32d526c --- /dev/null +++ b/src/webpack/buildFilesParallel.js @@ -0,0 +1,43 @@ +const path = require("path"); +const removeTempRichmediaRc = require("../util/removeTempRichmediaRc"); + +const cliProgress = require("cli-progress"); + +const workerFarm = require('worker-farm'); + +module.exports = async function buildFiles(result, options) { + const webpackRun = workerFarm( + { + autoStart: true, + maxRetries: 0, + maxConcurrentCallsPerWorker: 1, + maxConcurrentWorkers: options.parallel === true ? 4 : options.parallel + }, + require.resolve('./webpackRun') + ); + + const startTime = new Date().getTime(); + const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic); + progressBar.start(result.length, 0); + + await Promise.all(result.map((result) => { + delete result.settings.row + return new Promise(res => webpackRun({ config: result.settings, options }, () => { + progressBar.increment() + res() + })); + })); + + workerFarm.end(webpackRun) + + progressBar.stop(); + console.log(`built in ${new Date().getTime() - startTime}ms`); + + // final clean up + console.log("Removing temp .richmediarc..."); + removeTempRichmediaRc(result); + + return { + outputDir: path.resolve(options.outputDir), + }; +}; diff --git a/src/webpack/webpackRun.js b/src/webpack/webpackRun.js new file mode 100644 index 0000000..703fe86 --- /dev/null +++ b/src/webpack/webpackRun.js @@ -0,0 +1,37 @@ +const webpack = require('webpack') +const chalk = require('chalk') + +const createConfigByRichmediarcList = require('./config/createConfigByRichmediarcList') + +module.exports = async function ({ config, options }, cb) { + + const webpackConfig = await createConfigByRichmediarcList([config], options); + + webpack(webpackConfig[0]).run((err, stats) => { + if (err) { + console.error(err.stack || err); + if (err.details) { + err.details.forEach((item, index) => { + console.error(index, item.message); + }); + } + return; + } + + const info = stats.toJson(); + + if (stats.hasErrors()) { + info.errors.forEach((item, index) => { + console.log(chalk.red(item.message)); + }); + } + + if (stats.hasWarnings()) { + info.warnings.forEach((item) => { + console.log(chalk.green(item.message)); + }); + } + + cb(); + }); +} From 9199953a5b44e9e8d95b153038938ff1ef25a209 Mon Sep 17 00:00:00 2001 From: Vasyl Ilba Date: Tue, 17 Oct 2023 01:05:14 +0300 Subject: [PATCH 06/14] new dev parallel process with worker farm (faster for big amounts); devtools don't work because of security and CORS --- src/index.js | 4 +- src/webpack/devServerParallel.js | 180 +++++++++++++++++++++++++++++++ src/webpack/devSubServer.js | 51 +++++++++ 3 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 src/webpack/devServerParallel.js create mode 100644 src/webpack/devSubServer.js diff --git a/src/index.js b/src/index.js index 81c4da4..c13e74c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ const getWebpackConfigs = require("./webpack/getWebpackConfigs"); const devServer = require("./webpack/devServer"); +const devServerParallel = require("./webpack/devServerParallel"); const buildFiles = require("./webpack/buildFiles"); const buildFilesParallel = require("./webpack/buildFilesParallel"); const buildPreview = require("./webpack/buildPreview"); @@ -11,7 +12,8 @@ module.exports = async function (options) { const webpackConfigs = !skipBuild ? await getWebpackConfigs(options) : null; if (mode === "development") { - await devServer(webpackConfigs.result, webpackConfigs.choices.openLocation); + if (parallel) await devServerParallel(webpackConfigs.result, webpackConfigs.choices.openLocation, options); + else await devServer(webpackConfigs.result, webpackConfigs.choices.openLocation); } else { if (!skipBuild) { if (parallel) await buildFilesParallel(webpackConfigs.result, options); diff --git a/src/webpack/devServerParallel.js b/src/webpack/devServerParallel.js new file mode 100644 index 0000000..af74bb8 --- /dev/null +++ b/src/webpack/devServerParallel.js @@ -0,0 +1,180 @@ +const path = require('path'); +const fs = require('fs-extra'); +const express = require('express'); +const portfinder = require('portfinder'); +const util = require('util'); +const chalk = require('chalk'); +const open = require('open'); + +const extendObject = require('../util/extendObject'); +const createObjectFromJSONPath = require('../util/createObjectFromJSONPath'); +const getDataFromGoogleSpreadsheet = require('../util/getDataFromGoogleSpreadsheet'); +const removeTempRichmediaRc = require('../util/removeTempRichmediaRc'); + +const getNameFromLocation = require('../util/getNameFromLocation'); + +const workerFarm = require('worker-farm'); + +/** + * + * @param {Array<{webpack: *, settings: {location, data}}>} configs + * @param {boolean} openLocation + * @param {{}} options + */ +module.exports = async function devServer(configs, openLocation = true, options) { + + const N_SUBSERVERS = options.parallel === true ? 4 : options.parallel + + const settingsList = configs.map(({ settings }) => settings); + const port = await portfinder.getPortPromise(); + + const devSubServer = workerFarm( + { + maxRetries: 0, + autoStart: true, + maxConcurrentCallsPerWorker: 1, + maxConcurrentWorkers: N_SUBSERVERS + }, + require.resolve('./devSubServer') + ); + + const httpLocation = `http://localhost:${port}`; + + console.log(`${chalk.blue('i')} Server running. Please go to ${httpLocation} +${chalk.grey.bold('-------------------------------------------------------')} +`); + + const app = express(); + + app.listen(port, () => {}); + + const ports = await new Promise(res => portfinder.getPorts(N_SUBSERVERS, {}, (err, ports) => res(ports))) + + await Promise.all(settingsList + // spread work evenly into chunks + .reduce((acc, v, i) => { + acc[i % N_SUBSERVERS].push(v) + return acc + }, Array.from({length: N_SUBSERVERS}, () => [])) + // if we have empty chunks (less than N_SUBSERVERS banners) + .filter(e => e.length) + // map to promises + .map(async (chunk, i) => { + // delete row since it's an object with constructor and we can't carry it to the thread + chunk = chunk.map(({row, ...config}) => config) + + const port = ports[i] + + chunk.forEach(config => { + const name = getNameFromLocation(config.location); + app.use(`/${name}/`, (req, res, next) => { + res.redirect(`http://localhost:${port}/${name}/`) + }) + }) + + return new Promise(res => devSubServer({configs: chunk, options, port}, res)) + })); + + app.use('/', express.static(path.join(__dirname, '../preview/dist'))); + + openLocation && open(httpLocation); + + app.get('/data/ads.json', (req, res) => { + res.json({ + isGoogleSpreadsheetBanner: typeof configs[0].settings.data.settings.contentSource !== 'undefined', + ads: settingsList.map(e => { + const assetName = getNameFromLocation(e.location) + const bundleName = e.data.settings.bundleName || getNameFromLocation(e.location) + const url = `${httpLocation}/${assetName}/index.html` + return { + url, + ...e.data.settings.size, + bundleName, + output: { + html: { + url, + }, + }, + } + }) + }) + }) + + app.get("/reload_dynamic_data", async function (req, res) { + const contentSource = configs[0].settings.data.settings.contentSource; + const spreadsheetData = await getDataFromGoogleSpreadsheet(contentSource); + + configs.forEach(config => { + let row = spreadsheetData.rows[config.settings.row.rowNumber-2]; //for example, row number 2 is array element 0 + + const staticRow = spreadsheetData.headerValues.reduce((prev, name) => { + prev[name] = row[name]; + return prev; + }, {}); + + let staticRowObject = {}; + for (const key in staticRow) { + if (staticRow.hasOwnProperty(key)) { + let obj = createObjectFromJSONPath(key, staticRow[key]); + extendObject(staticRowObject, obj); + } + } + + // filter out everything that is not needed. + if (config.settings.data.settings.contentSource.filter) { + const filters = []; + if (config.settings.data.settings.contentSource.filter instanceof Array) { + filters.push(...config.settings.data.settings.contentSource.filter); + } else { + filters.push(config.settings.data.settings.contentSource.filter); + } + + // for loop so i can break or return emmediatly + for (let j = 0; j < filters.length; j++) { + const filter = filters[j]; + for (const key in filter) { + if (filter.hasOwnProperty(key) && staticRowObject[key] && staticRowObject[key] !== filter[key]) { + return; + } + } + } + } + + // new content object with updated content from sheet + let content = extendObject({}, (config.settings.data.content || {}), staticRowObject) + + // next 4 lines is reading existing richmediarc from the disk, updating the content object, and then writing the new file to disk again + const configFile = fs.readFileSync(config.settings.location, {encoding:'utf8', flag:'r'}) + const configFileJson = JSON.parse(configFile); + + if (!util.isDeepStrictEqual(configFileJson.content, content)) { //compare 'new' content with old content. If anything has changed, write a new file + configFileJson.content = content; + fs.writeFileSync(config.settings.location, Buffer.from(JSON.stringify(configFileJson))); + } + }) + + res.send('ok'); + }); + + // eslint-disable-next-line + process.stdin.resume(); //so the program will not close instantly + + function exitHandler(options, exitCode) { + if (options.cleanup) removeTempRichmediaRc(configs); + if (exitCode || exitCode === 0) console.log(exitCode); + if (options.exit) process.exit(); + } + + //do something when app is closing + process.on('exit', exitHandler.bind(null,{cleanup:true})); + + //catches ctrl+c event + process.on('SIGINT', exitHandler.bind(null, {exit:true})); + + // catches "kill pid" (for example: nodemon restart) + process.on('SIGUSR1', exitHandler.bind(null, {exit:true})); + process.on('SIGUSR2', exitHandler.bind(null, {exit:true})); + + //catches uncaught exceptions + process.on('uncaughtException', exitHandler.bind(null, {exit:true})); +}; diff --git a/src/webpack/devSubServer.js b/src/webpack/devSubServer.js new file mode 100644 index 0000000..bd1d525 --- /dev/null +++ b/src/webpack/devSubServer.js @@ -0,0 +1,51 @@ +const webpack = require('webpack'); +const webpackHotMiddleware = require('webpack-hot-middleware'); +const webpackDevMiddleware = require('webpack-dev-middleware'); +const express = require('express'); + +const createConfigByRichmediarcList = require('./config/createConfigByRichmediarcList') + +const getNameFromLocation = require('../util/getNameFromLocation'); + +/** + * + * @param {Array<{settings: {location, data}}>} configs + * @param {{}} options + * @param {number} port + */ +module.exports = async function devSubServer({configs, options, port}, cb) { + const webpackConfigList = await createConfigByRichmediarcList(configs, options); + const settingsList = configs; + + const app = express(); + + webpackConfigList.forEach((config, index) => { + const hmrPath = '__webpack_hmr'; + const name = getNameFromLocation(settingsList[index].location); + + config.mode = 'development'; + + config.output = { + ...config.output, + hotUpdateChunkFilename: '.hot/.hot-update.js', + hotUpdateMainFilename: '.hot/.hot-update.json', + }; + + const compiler = webpack(config, () => {}); + + app.use( + webpackDevMiddleware(compiler, { + publicPath: `/${name}/`, + }), + ); + + app.use( + webpackHotMiddleware(compiler, { + path: `/${name}/${hmrPath}`, + }), + ); + }); + + app.listen(port, () => {}); + cb(); +}; From bdfb1ac9ad9295e6de0e5c96f71076a0db3fd6dc Mon Sep 17 00:00:00 2001 From: Vasyl Ilba Date: Tue, 17 Oct 2023 12:27:49 +0300 Subject: [PATCH 07/14] fixes dynamic data comparison for dev --- src/webpack/devServer.js | 1 + src/webpack/devServerParallel.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/webpack/devServer.js b/src/webpack/devServer.js index dcea8f3..8bdd9cc 100644 --- a/src/webpack/devServer.js +++ b/src/webpack/devServer.js @@ -138,6 +138,7 @@ ${chalk.grey.bold('-------------------------------------------------------')} // next 4 lines is reading existing richmediarc from the disk, updating the content object, and then writing the new file to disk again const configFile = fs.readFileSync(config.settings.location, {encoding:'utf8', flag:'r'}) const configFileJson = JSON.parse(configFile); + content = JSON.parse(JSON.stringify(content)) if (!util.isDeepStrictEqual(configFileJson.content, content)) { //compare 'new' content with old content. If anything has changed, write a new file configFileJson.content = content; diff --git a/src/webpack/devServerParallel.js b/src/webpack/devServerParallel.js index af74bb8..61c909b 100644 --- a/src/webpack/devServerParallel.js +++ b/src/webpack/devServerParallel.js @@ -146,6 +146,7 @@ ${chalk.grey.bold('-------------------------------------------------------')} // next 4 lines is reading existing richmediarc from the disk, updating the content object, and then writing the new file to disk again const configFile = fs.readFileSync(config.settings.location, {encoding:'utf8', flag:'r'}) const configFileJson = JSON.parse(configFile); + content = JSON.parse(JSON.stringify(content)) if (!util.isDeepStrictEqual(configFileJson.content, content)) { //compare 'new' content with old content. If anything has changed, write a new file configFileJson.content = content; From 0b3c9e838056925f901c1e4aafa2ee296d96f308 Mon Sep 17 00:00:00 2001 From: Vasyl Ilba Date: Wed, 18 Oct 2023 23:59:37 +0300 Subject: [PATCH 08/14] waiting for webpack to dev all configs before opening the browser --- src/webpack/devServerParallel.js | 2 +- src/webpack/devSubServer.js | 32 +++++++++++++++++--------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/webpack/devServerParallel.js b/src/webpack/devServerParallel.js index 61c909b..bdfeb5c 100644 --- a/src/webpack/devServerParallel.js +++ b/src/webpack/devServerParallel.js @@ -59,7 +59,7 @@ ${chalk.grey.bold('-------------------------------------------------------')} // if we have empty chunks (less than N_SUBSERVERS banners) .filter(e => e.length) // map to promises - .map(async (chunk, i) => { + .map((chunk, i) => { // delete row since it's an object with constructor and we can't carry it to the thread chunk = chunk.map(({row, ...config}) => config) diff --git a/src/webpack/devSubServer.js b/src/webpack/devSubServer.js index bd1d525..c7eb02e 100644 --- a/src/webpack/devSubServer.js +++ b/src/webpack/devSubServer.js @@ -19,7 +19,7 @@ module.exports = async function devSubServer({configs, options, port}, cb) { const app = express(); - webpackConfigList.forEach((config, index) => { + await Promise.all(webpackConfigList.map((config, index) => { const hmrPath = '__webpack_hmr'; const name = getNameFromLocation(settingsList[index].location); @@ -31,20 +31,22 @@ module.exports = async function devSubServer({configs, options, port}, cb) { hotUpdateMainFilename: '.hot/.hot-update.json', }; - const compiler = webpack(config, () => {}); - - app.use( - webpackDevMiddleware(compiler, { - publicPath: `/${name}/`, - }), - ); - - app.use( - webpackHotMiddleware(compiler, { - path: `/${name}/${hmrPath}`, - }), - ); - }); + return new Promise(res => { + const compiler = webpack(config, res); + + app.use( + webpackDevMiddleware(compiler, { + publicPath: `/${name}/`, + }), + ); + + app.use( + webpackHotMiddleware(compiler, { + path: `/${name}/${hmrPath}`, + }), + ); + }) + })); app.listen(port, () => {}); cb(); From 6d7fe59091bb57d998d4f8b2a550652efd849b9b Mon Sep 17 00:00:00 2001 From: Vasyl Ilba Date: Fri, 20 Oct 2023 01:50:56 +0300 Subject: [PATCH 09/14] fixes preview sorting --- src/index.js | 2 +- src/webpack/buildPreview.js | 21 ++++----------------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/src/index.js b/src/index.js index c13e74c..801b045 100644 --- a/src/index.js +++ b/src/index.js @@ -19,6 +19,6 @@ module.exports = async function (options) { if (parallel) await buildFilesParallel(webpackConfigs.result, options); else await buildFiles(webpackConfigs.result, outputDir); } - if (!skipPreview) await buildPreview(outputDir); + if (!skipPreview) await buildPreview(webpackConfigs?.result, outputDir); } }; diff --git a/src/webpack/buildPreview.js b/src/webpack/buildPreview.js index 047bae0..0a78ffe 100644 --- a/src/webpack/buildPreview.js +++ b/src/webpack/buildPreview.js @@ -1,32 +1,19 @@ const fs = require("fs-extra"); const path = require("path"); - -const chalk = require("chalk"); -const webpack = require("webpack"); -// const HtmlWebpackPlugin = require("html-webpack-plugin"); const archiver = require("archiver"); const globPromise = require("glob-promise"); -const cliProgress = require("cli-progress"); - -// const getTemplate = require("../util/getPreviewTemplate"); -const removeTempRichmediaRc = require("../util/removeTempRichmediaRc"); const getNameFromLocation = require("../util/getNameFromLocation"); -// const previewWebackConfig = require("../preview/webpack.config"); const htmlParser = require("node-html-parser"); - -const getFilesizeInBytes = (filename) => { - var stats = fs.statSync(filename); - var fileSizeInBytes = stats.size; - return fileSizeInBytes; -}; - module.exports = async function buildPreview(result, outputDir) { // find all ads in directory const allIndexHtmlFiles = await globPromise(`${outputDir}/**/index.html`); allIndexHtmlFiles.sort() - result && result.sort((a, b) => a.settings.data.settings.bundleName > b.settings.data.settings.bundleName ? 1 : -1) + if (result) { + result.forEach(e => e.settings.data.settings.bundleName ??= getNameFromLocation(e.settings.location)) + result.sort((a, b) => a.settings.data.settings.bundleName > b.settings.data.settings.bundleName ? 1 : -1) + } const allAds = allIndexHtmlFiles.reduce((acc, filename, i) => { const rawData = fs.readFileSync(filename, "utf8"); From a2442adb7dfff81f7a936db4eaf265102725f1f8 Mon Sep 17 00:00:00 2001 From: Vasyl Ilba Date: Fri, 20 Oct 2023 01:58:10 +0300 Subject: [PATCH 10/14] bring back timestamp tooltip on preview and required webpack-cli in package --- src/preview/dist/js/index_bundle.js | 2 +- src/preview/package-lock.json | 80 +++++++++++++------------- src/preview/package.json | 11 ++-- src/preview/src/components/Previews.js | 8 ++- 4 files changed, 51 insertions(+), 50 deletions(-) diff --git a/src/preview/dist/js/index_bundle.js b/src/preview/dist/js/index_bundle.js index 74ad792..a7df369 100644 --- a/src/preview/dist/js/index_bundle.js +++ b/src/preview/dist/js/index_bundle.js @@ -3437,7 +3437,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ Previews)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _Previews_module_scss__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Previews.module.scss */ \"./src/components/Previews.module.scss\");\n/* harmony import */ var react_router_dom__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! react-router-dom */ \"./node_modules/react-router-dom/dist/index.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/AppBar/AppBar.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/Toolbar/Toolbar.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/Box/Box.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/Tooltip/Tooltip.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/Button/Button.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/Typography/Typography.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/FormControl/FormControl.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/InputLabel/InputLabel.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/Select/Select.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/OutlinedInput/OutlinedInput.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/Chip/Chip.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/ListSubheader/ListSubheader.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/MenuItem/MenuItem.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/TablePagination/TablePagination.js\");\n/* harmony import */ var _mui_icons_material_Cancel__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! @mui/icons-material/Cancel */ \"./node_modules/@mui/icons-material/Cancel.js\");\n/* harmony import */ var _mui_icons_material_Cached__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! @mui/icons-material/Cached */ \"./node_modules/@mui/icons-material/Cached.js\");\n/* harmony import */ var _AdPreview__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./AdPreview */ \"./src/components/AdPreview.js\");\n\n\n\n\n\n\n\n\nconst paginate = (array, page_size, page_number) => {\n return array.slice((page_number - 1) * page_size, page_number * page_size);\n};\nconst getFiltersFromAds = (ads, searchParams) => {\n // returns array with all the filtergroup arrays containing all the unique filters\n // it also initially sets the filters based on the searchparams\n let filterGroups = [];\n ads.forEach(ad => {\n const bundleSplits = ad.bundleName.split(\"_\");\n bundleSplits.forEach((bundleSplit, index) => {\n if (!filterGroups[index]) filterGroups[index] = [];\n filterGroups[index].push(bundleSplit);\n });\n });\n filterGroups = filterGroups.map(filterGroup => [...new Set(filterGroup)]); // make them all unique\n\n // get the initial filter(s) from the searchParams ?filter=hk,en;friendsfamily;160x600\n const searchParamsArray = searchParams.get(\"filter\") ? searchParams.get(\"filter\").split(\";\").map(filterGroup => filterGroup.split(\",\")) : [];\n return filterGroups.map(filterGroup => {\n return filterGroup.map(filter => {\n return {\n value: filter,\n selected: searchParamsArray.flat().includes(filter)\n };\n });\n });\n};\nconst composeSearchParamsFromFilters = filters => {\n return filters.map(filterGroup => {\n return filterGroup.filter(filter => filter.selected).map(filter => filter.value).join(\",\");\n }).filter(filterGroup => filterGroup.length > 0).join(\";\");\n};\nconst getAdsListFromFilters = (adsList, filters) => {\n return adsList.filter(ad => {\n // en_friendsfamily_ill_300x250\n return ad.bundleName.split(\"_\").every((bundleSplit, index) => {\n const isFilteringOnGroup = filters[index].filter(filter => filter.selected).length > 0; // if any of the 'selected' keys are true in these objects, it means we're filtering on that group.\n\n if (isFilteringOnGroup) {\n const [filterValue] = filters[index].filter(filter => filter.value === bundleSplit).map(filter => filter.selected);\n return filterValue;\n } else {\n return true; // if we're not filtering on this group, all keys in this group are allowed (because no specific filter is selected in that group)\n }\n });\n });\n};\n\nconst getLabelFromFilterGroup = filterGroup => {\n if (filterGroup.every(filter => filter.value.match(/[0-9]+x[0-9]+/i))) return \"Dimensions\"; // if it matches dimensions, like 300x500 ;\n if (filterGroup.every(filter => filter.value.match(/^[a-z]{2}$/i))) return \"Language\"; // if string is 2 chars long and a-z or A-Z\n return \"Category\";\n};\nfunction Previews({\n data\n}) {\n const [searchParams, setSearchParams] = (0,react_router_dom__WEBPACK_IMPORTED_MODULE_3__.useSearchParams)();\n const gsdevtools = (0,react__WEBPACK_IMPORTED_MODULE_0__.useMemo)(() => searchParams.get('gsdevtools'), []);\n const [ads, setAds] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([]);\n const [filters, setFilters] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(getFiltersFromAds(data.ads, searchParams));\n const [page, setPage] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(0);\n const [itemsPerPage, setItemsPerPage] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(10);\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {\n setAds(getAdsListFromFilters(data.ads, filters));\n const filter = decodeURI(composeSearchParamsFromFilters(filters));\n const collectFilters = {};\n filter && (collectFilters.filter = filter);\n gsdevtools && (collectFilters.gsdevtools = gsdevtools);\n setSearchParams(collectFilters);\n setPage(0);\n }, [filters]);\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {\n if (gsdevtools !== \"true\") return;\n window.addEventListener('keydown', e => {\n if (e.defaultPrevented) return;\n if (e.key === \" \") {\n e.preventDefault();\n }\n });\n }, []);\n const getSelectedFilters = () => {\n // returns flat array of selected filters i.e. [\"en\",\"300x400\"] (the input element needs this as a value)\n return filters.flat().filter(filter => filter.selected).map(filter => filter.value);\n };\n const handleChangeFilter = event => {\n // make deep copy of filters state\n let updatedFilters = JSON.parse(JSON.stringify(filters));\n\n // set each filter's selected value based on the value from the event\n updatedFilters.flat().forEach(filter => {\n filter.selected = event.target.value.includes(filter.value);\n });\n setFilters(updatedFilters);\n };\n function handleFilterDelete(e, value) {\n let updatedFilters = JSON.parse(JSON.stringify(filters));\n\n // set each filter's selected value based on the value from the event\n updatedFilters.flat().forEach(filter => {\n if (filter.value === value) {\n console.log(\"found the one to be deleted\");\n console.log(filter);\n filter.selected = false;\n }\n });\n setFilters(updatedFilters);\n }\n\n // handle button(s)\n\n const handleDownloadZips = event => {\n console.log(event);\n window.open(\"all.zip\");\n };\n const handleReloadDynamicData = async e => {\n const res = await fetch('reload_dynamic_data');\n if (res.status === 200) location.reload();\n };\n\n // handle pages\n const handleChangePage = (event, newPage) => {\n setPage(newPage);\n };\n const handleChangeRowsPerPage = event => {\n setItemsPerPage(parseInt(event.target.value, 10));\n setPage(0);\n };\n const pageAds = (0,react__WEBPACK_IMPORTED_MODULE_0__.useMemo)(() => {\n return paginate(ads, itemsPerPage, page + 1);\n }, [page, itemsPerPage, ads]);\n\n // generate page\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_4__[\"default\"], {\n position: \"sticky\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__[\"default\"], {\n className: _Previews_module_scss__WEBPACK_IMPORTED_MODULE_1__[\"default\"].toolbar\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__[\"default\"], {\n sx: {\n display: 'flex',\n gap: '10px'\n }\n }, data.isGoogleSpreadsheetBanner ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__[\"default\"], {\n title: \"Reload dynamic data\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__[\"default\"], {\n onClick: handleReloadDynamicData,\n color: \"inherit\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_Cached__WEBPACK_IMPORTED_MODULE_9__[\"default\"], null))) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_10__[\"default\"], {\n align: \"left\",\n variant: \"h5\",\n component: \"div\"\n }, \"Preview\")), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__[\"default\"], {\n sx: {\n m: 1,\n minWidth: 150,\n maxWidth: \"40%\"\n }\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__[\"default\"], {\n id: \"demo-multiple-chip-label\"\n }, \"Filters\"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__[\"default\"], {\n labelId: \"demo-multiple-chip-label\",\n id: \"demo-multiple-chip\",\n multiple: true,\n value: getSelectedFilters(),\n onChange: handleChangeFilter,\n input: /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__[\"default\"], {\n id: \"select-multiple-chip\",\n label: \"Filters\"\n }),\n renderValue: selected => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__[\"default\"], {\n sx: {\n display: \"flex\",\n flexWrap: \"wrap\",\n gap: 0.5\n }\n }, selected.map(value => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__[\"default\"], {\n onDelete: e => handleFilterDelete(e, value),\n key: value,\n label: value,\n deleteIcon: /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_Cancel__WEBPACK_IMPORTED_MODULE_16__[\"default\"], {\n onMouseDown: event => event.stopPropagation()\n })\n })))\n }, filters.filter(filterGroup => filterGroup.length > 1) // only show filtergroups with more than 1 filter\n .map((filterGroup, filterGroupIndex) => [/*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__[\"default\"], null, getLabelFromFilterGroup(filterGroup)), filterGroup.map((filter, filterIndex) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__[\"default\"], {\n key: filter.value,\n value: filter.value\n }, filter.value))]))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__[\"default\"], {\n labelRowsPerPage: \"Ads per page:\",\n component: \"div\",\n count: ads.length,\n page: page,\n onPageChange: handleChangePage,\n rowsPerPage: itemsPerPage,\n onRowsPerPageChange: handleChangeRowsPerPage\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__[\"default\"], {\n onClick: handleDownloadZips,\n color: \"inherit\"\n }, \"Download Zips\"))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n className: _Previews_module_scss__WEBPACK_IMPORTED_MODULE_1__[\"default\"].previews\n }, pageAds.length > 0 && pageAds.map(ad => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_AdPreview__WEBPACK_IMPORTED_MODULE_2__.AdPreview, {\n gsdevtools: gsdevtools,\n key: ad.bundleName,\n ad: ad,\n maxFileSize: ad.maxFileSize,\n timestamp: data.timestamp\n })), pageAds.length < 1 && \"No ads found with the current combination of filters\"));\n}\n\n//# sourceURL=webpack://display-dev-server-preview/./src/components/Previews.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ Previews)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _Previews_module_scss__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Previews.module.scss */ \"./src/components/Previews.module.scss\");\n/* harmony import */ var react_router_dom__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! react-router-dom */ \"./node_modules/react-router-dom/dist/index.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/AppBar/AppBar.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/Toolbar/Toolbar.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/Box/Box.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/Tooltip/Tooltip.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/Button/Button.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/Typography/Typography.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/FormControl/FormControl.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/InputLabel/InputLabel.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/Select/Select.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/OutlinedInput/OutlinedInput.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/Chip/Chip.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/ListSubheader/ListSubheader.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/MenuItem/MenuItem.js\");\n/* harmony import */ var _mui_material__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! @mui/material */ \"./node_modules/@mui/material/TablePagination/TablePagination.js\");\n/* harmony import */ var _mui_icons_material_Cancel__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! @mui/icons-material/Cancel */ \"./node_modules/@mui/icons-material/Cancel.js\");\n/* harmony import */ var _mui_icons_material_Cached__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! @mui/icons-material/Cached */ \"./node_modules/@mui/icons-material/Cached.js\");\n/* harmony import */ var _AdPreview__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./AdPreview */ \"./src/components/AdPreview.js\");\n\n\n\n\n\n\n\n\nconst paginate = (array, page_size, page_number) => {\n return array.slice((page_number - 1) * page_size, page_number * page_size);\n};\nconst getFiltersFromAds = (ads, searchParams) => {\n // returns array with all the filtergroup arrays containing all the unique filters\n // it also initially sets the filters based on the searchparams\n let filterGroups = [];\n ads.forEach(ad => {\n const bundleSplits = ad.bundleName.split(\"_\");\n bundleSplits.forEach((bundleSplit, index) => {\n if (!filterGroups[index]) filterGroups[index] = [];\n filterGroups[index].push(bundleSplit);\n });\n });\n filterGroups = filterGroups.map(filterGroup => [...new Set(filterGroup)]); // make them all unique\n\n // get the initial filter(s) from the searchParams ?filter=hk,en;friendsfamily;160x600\n const searchParamsArray = searchParams.get(\"filter\") ? searchParams.get(\"filter\").split(\";\").map(filterGroup => filterGroup.split(\",\")) : [];\n return filterGroups.map(filterGroup => {\n return filterGroup.map(filter => {\n return {\n value: filter,\n selected: searchParamsArray.flat().includes(filter)\n };\n });\n });\n};\nconst composeSearchParamsFromFilters = filters => {\n return filters.map(filterGroup => {\n return filterGroup.filter(filter => filter.selected).map(filter => filter.value).join(\",\");\n }).filter(filterGroup => filterGroup.length > 0).join(\";\");\n};\nconst getAdsListFromFilters = (adsList, filters) => {\n return adsList.filter(ad => {\n // en_friendsfamily_ill_300x250\n return ad.bundleName.split(\"_\").every((bundleSplit, index) => {\n const isFilteringOnGroup = filters[index].filter(filter => filter.selected).length > 0; // if any of the 'selected' keys are true in these objects, it means we're filtering on that group.\n\n if (isFilteringOnGroup) {\n const [filterValue] = filters[index].filter(filter => filter.value === bundleSplit).map(filter => filter.selected);\n return filterValue;\n } else {\n return true; // if we're not filtering on this group, all keys in this group are allowed (because no specific filter is selected in that group)\n }\n });\n });\n};\n\nconst getLabelFromFilterGroup = filterGroup => {\n if (filterGroup.every(filter => filter.value.match(/[0-9]+x[0-9]+/i))) return \"Dimensions\"; // if it matches dimensions, like 300x500 ;\n if (filterGroup.every(filter => filter.value.match(/^[a-z]{2}$/i))) return \"Language\"; // if string is 2 chars long and a-z or A-Z\n return \"Category\";\n};\nfunction Previews({\n data\n}) {\n const [searchParams, setSearchParams] = (0,react_router_dom__WEBPACK_IMPORTED_MODULE_3__.useSearchParams)();\n const gsdevtools = (0,react__WEBPACK_IMPORTED_MODULE_0__.useMemo)(() => searchParams.get('gsdevtools'), []);\n const [ads, setAds] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)([]);\n const [filters, setFilters] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(getFiltersFromAds(data.ads, searchParams));\n const [page, setPage] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(0);\n const [itemsPerPage, setItemsPerPage] = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(10);\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {\n setAds(getAdsListFromFilters(data.ads, filters));\n const filter = decodeURI(composeSearchParamsFromFilters(filters));\n const collectFilters = {};\n filter && (collectFilters.filter = filter);\n gsdevtools && (collectFilters.gsdevtools = gsdevtools);\n setSearchParams(collectFilters);\n setPage(0);\n }, [filters]);\n (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {\n if (gsdevtools !== \"true\") return;\n window.addEventListener('keydown', e => {\n if (e.defaultPrevented) return;\n if (e.key === \" \") {\n e.preventDefault();\n }\n });\n }, []);\n const getSelectedFilters = () => {\n // returns flat array of selected filters i.e. [\"en\",\"300x400\"] (the input element needs this as a value)\n return filters.flat().filter(filter => filter.selected).map(filter => filter.value);\n };\n const handleChangeFilter = event => {\n // make deep copy of filters state\n let updatedFilters = JSON.parse(JSON.stringify(filters));\n\n // set each filter's selected value based on the value from the event\n updatedFilters.flat().forEach(filter => {\n filter.selected = event.target.value.includes(filter.value);\n });\n setFilters(updatedFilters);\n };\n function handleFilterDelete(e, value) {\n let updatedFilters = JSON.parse(JSON.stringify(filters));\n\n // set each filter's selected value based on the value from the event\n updatedFilters.flat().forEach(filter => {\n if (filter.value === value) {\n filter.selected = false;\n }\n });\n setFilters(updatedFilters);\n }\n\n // handle button(s)\n\n const handleDownloadZips = event => {\n // console.log(event);\n window.open(\"all.zip\");\n };\n const handleReloadDynamicData = async e => {\n const res = await fetch('reload_dynamic_data');\n if (res.status === 200) location.reload();\n };\n\n // handle pages\n const handleChangePage = (event, newPage) => {\n setPage(newPage);\n };\n const handleChangeRowsPerPage = event => {\n setItemsPerPage(parseInt(event.target.value, 10));\n setPage(0);\n };\n const pageAds = (0,react__WEBPACK_IMPORTED_MODULE_0__.useMemo)(() => {\n return paginate(ads, itemsPerPage, page + 1);\n }, [page, itemsPerPage, ads]);\n\n // generate page\n return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_4__[\"default\"], {\n position: \"sticky\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_5__[\"default\"], {\n className: _Previews_module_scss__WEBPACK_IMPORTED_MODULE_1__[\"default\"].toolbar\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__[\"default\"], {\n sx: {\n display: 'flex',\n gap: '10px'\n }\n }, data.isGoogleSpreadsheetBanner ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__[\"default\"], {\n title: \"Reload dynamic data\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__[\"default\"], {\n onClick: handleReloadDynamicData,\n color: \"inherit\"\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_Cached__WEBPACK_IMPORTED_MODULE_9__[\"default\"], null))) : /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_7__[\"default\"], {\n title: new Date(data.timestamp).toLocaleString()\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_10__[\"default\"], {\n align: \"left\",\n variant: \"h5\",\n component: \"div\"\n }, \"Preview\"))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_11__[\"default\"], {\n sx: {\n m: 1,\n minWidth: 150,\n maxWidth: \"40%\"\n }\n }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_12__[\"default\"], {\n id: \"demo-multiple-chip-label\"\n }, \"Filters\"), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_13__[\"default\"], {\n labelId: \"demo-multiple-chip-label\",\n id: \"demo-multiple-chip\",\n multiple: true,\n value: getSelectedFilters(),\n onChange: handleChangeFilter,\n input: /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_14__[\"default\"], {\n id: \"select-multiple-chip\",\n label: \"Filters\"\n }),\n renderValue: selected => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_6__[\"default\"], {\n sx: {\n display: \"flex\",\n flexWrap: \"wrap\",\n gap: 0.5\n }\n }, selected.map(value => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_15__[\"default\"], {\n onDelete: e => handleFilterDelete(e, value),\n key: value,\n label: value,\n deleteIcon: /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_icons_material_Cancel__WEBPACK_IMPORTED_MODULE_16__[\"default\"], {\n onMouseDown: event => event.stopPropagation()\n })\n })))\n }, filters.filter(filterGroup => filterGroup.length > 1) // only show filtergroups with more than 1 filter\n .map((filterGroup, filterGroupIndex) => [/*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_17__[\"default\"], null, getLabelFromFilterGroup(filterGroup)), filterGroup.map((filter, filterIndex) => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_18__[\"default\"], {\n key: filter.value,\n value: filter.value\n }, filter.value))]))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_19__[\"default\"], {\n labelRowsPerPage: \"Ads per page:\",\n component: \"div\",\n count: ads.length,\n page: page,\n onPageChange: handleChangePage,\n rowsPerPage: itemsPerPage,\n onRowsPerPageChange: handleChangeRowsPerPage\n }), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_mui_material__WEBPACK_IMPORTED_MODULE_8__[\"default\"], {\n onClick: handleDownloadZips,\n color: \"inherit\"\n }, \"Download Zips\"))), /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(\"div\", {\n className: _Previews_module_scss__WEBPACK_IMPORTED_MODULE_1__[\"default\"].previews\n }, pageAds.length > 0 && pageAds.map(ad => /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_AdPreview__WEBPACK_IMPORTED_MODULE_2__.AdPreview, {\n gsdevtools: gsdevtools,\n key: ad.bundleName,\n ad: ad,\n maxFileSize: ad.maxFileSize,\n timestamp: data.timestamp\n })), pageAds.length < 1 && \"No ads found with the current combination of filters\"));\n}\n\n//# sourceURL=webpack://display-dev-server-preview/./src/components/Previews.js?"); /***/ }), diff --git a/src/preview/package-lock.json b/src/preview/package-lock.json index 0ba9901..7a2e266 100644 --- a/src/preview/package-lock.json +++ b/src/preview/package-lock.json @@ -23,7 +23,7 @@ "sass-loader": "^13.2.0", "style-loader": "^3.3.1", "webpack": "^5.75.0", - "webpack-cli": "^5.0.1", + "webpack-cli": "^5.1.4", "webpack-dev-server": "^4.11.1" } }, @@ -1455,9 +1455,9 @@ } }, "node_modules/@webpack-cli/configtest": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.0.1.tgz", - "integrity": "sha512-njsdJXJSiS2iNbQVS0eT8A/KPnmyH4pv1APj2K0d1wrZcBLw+yppxOy4CGqa0OxDJkzfL/XELDhD8rocnIwB5A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", "dev": true, "engines": { "node": ">=14.15.0" @@ -1468,9 +1468,9 @@ } }, "node_modules/@webpack-cli/info": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.1.tgz", - "integrity": "sha512-fE1UEWTwsAxRhrJNikE7v4EotYflkEhBL7EbajfkPlf6E37/2QshOy/D48Mw8G5XMFlQtS6YV42vtbG9zBpIQA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", "dev": true, "engines": { "node": ">=14.15.0" @@ -1481,9 +1481,9 @@ } }, "node_modules/@webpack-cli/serve": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.1.tgz", - "integrity": "sha512-0G7tNyS+yW8TdgHwZKlDWYXFA6OJQnoLCQvYKkQP0Q2X205PSQ6RNUj0M+1OB/9gRQaUZ/ccYfaxd0nhaWKfjw==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", "dev": true, "engines": { "node": ">=14.15.0" @@ -5371,17 +5371,17 @@ } }, "node_modules/webpack-cli": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz", - "integrity": "sha512-S3KVAyfwUqr0Mo/ur3NzIp6jnerNpo7GUO6so51mxLi1spqsA17YcMXy0WOIJtBSnj748lthxC6XLbNKh/ZC+A==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^2.0.1", - "@webpack-cli/info": "^2.0.1", - "@webpack-cli/serve": "^2.0.1", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", "colorette": "^2.0.14", - "commander": "^9.4.1", + "commander": "^10.0.1", "cross-spawn": "^7.0.3", "envinfo": "^7.7.3", "fastest-levenshtein": "^1.0.12", @@ -5416,12 +5416,12 @@ } }, "node_modules/webpack-cli/node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true, "engines": { - "node": "^12.20.0 || >=14" + "node": ">=14" } }, "node_modules/webpack-dev-middleware": { @@ -6811,23 +6811,23 @@ } }, "@webpack-cli/configtest": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.0.1.tgz", - "integrity": "sha512-njsdJXJSiS2iNbQVS0eT8A/KPnmyH4pv1APj2K0d1wrZcBLw+yppxOy4CGqa0OxDJkzfL/XELDhD8rocnIwB5A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", "dev": true, "requires": {} }, "@webpack-cli/info": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.1.tgz", - "integrity": "sha512-fE1UEWTwsAxRhrJNikE7v4EotYflkEhBL7EbajfkPlf6E37/2QshOy/D48Mw8G5XMFlQtS6YV42vtbG9zBpIQA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", "dev": true, "requires": {} }, "@webpack-cli/serve": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.1.tgz", - "integrity": "sha512-0G7tNyS+yW8TdgHwZKlDWYXFA6OJQnoLCQvYKkQP0Q2X205PSQ6RNUj0M+1OB/9gRQaUZ/ccYfaxd0nhaWKfjw==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", "dev": true, "requires": {} }, @@ -9733,17 +9733,17 @@ } }, "webpack-cli": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz", - "integrity": "sha512-S3KVAyfwUqr0Mo/ur3NzIp6jnerNpo7GUO6so51mxLi1spqsA17YcMXy0WOIJtBSnj748lthxC6XLbNKh/ZC+A==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, "requires": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^2.0.1", - "@webpack-cli/info": "^2.0.1", - "@webpack-cli/serve": "^2.0.1", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", "colorette": "^2.0.14", - "commander": "^9.4.1", + "commander": "^10.0.1", "cross-spawn": "^7.0.3", "envinfo": "^7.7.3", "fastest-levenshtein": "^1.0.12", @@ -9754,9 +9754,9 @@ }, "dependencies": { "commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "dev": true } } diff --git a/src/preview/package.json b/src/preview/package.json index 2fb76c0..601f69d 100644 --- a/src/preview/package.json +++ b/src/preview/package.json @@ -19,12 +19,11 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.8.2", + "sass": "^1.58.3", "sass-loader": "^13.2.0", "style-loader": "^3.3.1", "webpack": "^5.75.0", - "webpack-cli": "^5.0.1", - "webpack-dev-server": "^4.11.1", - "sass": "^1.58.3" - }, - "dependencies": {} -} \ No newline at end of file + "webpack-cli": "^5.1.4", + "webpack-dev-server": "^4.11.1" + } +} diff --git a/src/preview/src/components/Previews.js b/src/preview/src/components/Previews.js index 0093b58..7804831 100644 --- a/src/preview/src/components/Previews.js +++ b/src/preview/src/components/Previews.js @@ -186,9 +186,11 @@ export default function Previews({ data }) { : <> } - - Preview - + + + Preview + + From 8ec4cf5ac80c21f9e2496a575abd35d092cf90bb Mon Sep 17 00:00:00 2001 From: Vasyl Ilba Date: Fri, 20 Oct 2023 02:00:11 +0300 Subject: [PATCH 11/14] clear ads.json schema --- src/preview/public/data/ads.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/preview/public/data/ads.json b/src/preview/public/data/ads.json index ef83151..06f7279 100644 --- a/src/preview/public/data/ads.json +++ b/src/preview/public/data/ads.json @@ -1,6 +1,5 @@ { "isGoogleSpreadsheetBanner": false, - "maxFileSize": 150, "timestamp": 1691759644915, "ads": [ { From c07c2afdb4978e8c0af32cf310f53308c14f8eba Mon Sep 17 00:00:00 2001 From: Vasyl Ilba Date: Mon, 30 Oct 2023 13:36:08 +0200 Subject: [PATCH 12/14] detailed messages --- src/webpack/devServerParallel.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/webpack/devServerParallel.js b/src/webpack/devServerParallel.js index bdfeb5c..31c46f3 100644 --- a/src/webpack/devServerParallel.js +++ b/src/webpack/devServerParallel.js @@ -40,9 +40,14 @@ module.exports = async function devServer(configs, openLocation = true, options) const httpLocation = `http://localhost:${port}`; - console.log(`${chalk.blue('i')} Server running. Please go to ${httpLocation} + console.log(` +${chalk.blue('i')} ${openLocation + ? `Server ${httpLocation} will open automatically once everything is ready. This might take a while.` + : `Server ${httpLocation} running. It might take a while to load.` +} +${chalk.yellow('!')} Preview GSDevTools don't work on parallel. More info here: https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy ${chalk.grey.bold('-------------------------------------------------------')} -`); + `); const app = express(); From b193f36d726e5a713b8b07743386b7fc0a0308aa Mon Sep 17 00:00:00 2001 From: Vasyl Ilba Date: Mon, 30 Oct 2023 23:04:00 +0200 Subject: [PATCH 13/14] another 30%+ simple improvement, no double parallelism --- src/webpack/config/createConfig.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/webpack/config/createConfig.js b/src/webpack/config/createConfig.js index ceef907..97a8ae2 100644 --- a/src/webpack/config/createConfig.js +++ b/src/webpack/config/createConfig.js @@ -418,6 +418,7 @@ module.exports = function createConfig({ new TerserPlugin({ // minify: TerserPlugin.esbuildMinify, extractComments: false, + parallel: false, }) ); } From b89dd6b5fa769b3698938d02417813553cf1c4e2 Mon Sep 17 00:00:00 2001 From: Vasyl Ilba Date: Mon, 6 Nov 2023 12:34:22 +0200 Subject: [PATCH 14/14] new message for default dev --- src/webpack/devServer.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/webpack/devServer.js b/src/webpack/devServer.js index 8bdd9cc..bc6b833 100644 --- a/src/webpack/devServer.js +++ b/src/webpack/devServer.js @@ -34,9 +34,13 @@ module.exports = async function devServer(configs, openLocation = true) { open(`${httpLocation}?gsdevtools=true`); } - console.log(`${chalk.blue('i')} Server running. Please go to ${httpLocation} + console.log(` +${chalk.blue('i')} ${openLocation + ? `Server ${httpLocation} is running and will open automatically. It might take a while to load.` + : `Server ${httpLocation} is running. It might take a while to load.` +} ${chalk.grey.bold('-------------------------------------------------------')} -`); + `); const app = express();