Last active
February 15, 2023 15:29
-
-
Save romanlex/204e5e2d387c83a577f12a42f0bbc938 to your computer and use it in GitHub Desktop.
Revisions
-
romanlex revised this gist
Feb 15, 2023 . 1 changed file with 36 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,36 @@ import { createBrowserHistory } from 'history' const history = createBrowserHistory() const updater = new Updater() function moveWithReload() { const reqUrl = new Url(location.pathname, true) window.location.href = `${reqUrl.toString()}${location.search || ''}` } async function checkVersionApp(location) { // show preloader const reload = await updater.mustBeWithReload() if (reload) { moveWithReload(location) return } history.push(`${location.pathname}${location.search ?? ''}`, { __verified__: true }) // hide preloader } history.block((location) => { HistoryLogger.debug('Trying change location to:', location) if (Boolean(location?.state?.__verified__)) { HistoryLogger.debug('Target location is verified by previouse check. Unblock history change') return } checkVersionApp(location) return false }) export { history } -
romanlex revised this gist
Feb 15, 2023 . 1 changed file with 3 additions and 32 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,37 +1,8 @@ const { sources } = require('webpack') const { getCompilerHooks } = require('webpack-manifest-plugin') const PLUGIN_NAME = 'BundleManifestPlugin' /** @typedef {import("webpack").Compiler} Compiler */ /** @@ -93,12 +64,12 @@ class BundleManifestPlugin { }) beforeEmit.tap(PLUGIN_NAME, (manifest) => { this.manifest = manifest return manifest }) }) } } module.exports = BundleManifestPlugin -
romanlex revised this gist
Feb 15, 2023 . 2 changed files with 143 additions and 7 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,143 @@ // @flow import { ASSET_MANIFEST } from 'core/constants/webpack' import type { AssetsManifest } from 'core/types' import { Logger } from 'utils/logger' import { Storage } from 'utils/storage' import type { UpdaterServiceInterface } from './index.js.flow' const UpdaterLogger = Logger.get('Updater') type Config = {| manifestPath: string, |} /** * Сервис для обновления фронта на клиентах */ export class Updater implements UpdaterServiceInterface { static CHECK_TIME_LIMIT = 5 * 60 * 1000 static MANIFEST_LAST_CHECK_STORAGE_KEY = 'updater::manifestLastCheckAt' static MANIFEST_DIGEST_STORAGE_KEY = 'updater::manifestDigest' manifest: AssetsManifest manifestDigest: number = 0 _config: Config constructor(config: Config = { manifestPath: '/manifest.json' }) { this._config = config this.manifest = Updater.sortUnorderedKeys(ASSET_MANIFEST) UpdaterLogger.debug('built-in manifest', this.manifest) this.manifestDigest = this.hashCodeForString(JSON.stringify(this.manifest)) UpdaterLogger.debug('built-in manifest digest', this.manifestDigest) this.updateStorage(new Date().getTime(), this.manifestDigest) } static sortUnorderedKeys<T: { [key: string]: string }>(unordered: T): T { return Object.keys(unordered) .sort() .reduce((obj, key) => { obj[key] = unordered[key] return obj }, (({}: any): T)) } /** * Проверяет необходим или нет релоад страницы */ mustBeWithReload = async (): Promise<boolean> => { try { const _lastCheckAtStorage = parseInt(Storage.getItem(Updater.MANIFEST_LAST_CHECK_STORAGE_KEY) ?? 0, 10) const manifestDigestStorage = parseInt(Storage.getItem(Updater.MANIFEST_DIGEST_STORAGE_KEY), 10) const currentDate = new Date() const lastCheckAt = new Date(_lastCheckAtStorage) // если дайджест манифестов разный - это 100% протухшая апка if (!this.equalHashCodes(this.manifestDigest, manifestDigestStorage)) { return true } // если прошло меньше 5 минут - продолжаем переход дальше if (currentDate - lastCheckAt < Updater.CHECK_TIME_LIMIT) { return false } const newManifest = await this.getAppManifest() const appIsOutdated = this.equalHashCodes(this.manifestDigest, newManifest.manifestDigest) !== true if (appIsOutdated) { UpdaterLogger.debug('App is outdated') UpdaterLogger.debug('Reload window on history change') return true } else { this.updateStorage(new Date().getTime(), this.manifestDigest) return false } } catch (error) { // eslint-disable-next-line no-console console.error(error) } return false } updateStorage = (timestamp: number, digest: number) => { Storage.setItem(Updater.MANIFEST_LAST_CHECK_STORAGE_KEY, timestamp.toString()) Storage.setItem(Updater.MANIFEST_DIGEST_STORAGE_KEY, digest.toString()) } equalHashCodes = (a: number, b: number) => { return Math.abs(a) === Math.abs(b) } /** * Генерим хэшкод по строке * @param {string} строка от которой нужно сосчитать хэшкод */ hashCodeForString = (str: string) => { var hash = 0 for (var i = 0; i < str.length; i++) { var code = str.charCodeAt(i) hash = (hash << 5) - hash + code hash = hash & hash // Convert to 32bit integer } return hash } getCurrentManifest = async (): Promise<{ manifest: AssetsManifest, manifestDigest: number }> => { if (this.manifest) return { manifest: this.manifest, manifestDigest: this.manifestDigest } const manifest = await this.getAppManifest() return manifest } /** * Загружаем манифест */ getAppManifest = async (): Promise<{ manifest: AssetsManifest, manifestDigest: number }> => { const { manifestPath } = this._config const manifestResponse = await fetch(manifestPath, { cache: 'no-store' }) const manifestStr = await manifestResponse.text() let manifest: AssetsManifest let manifestDigest: number try { const parsed = JSON.parse(manifestStr) manifest = Updater.sortUnorderedKeys(parsed) manifestDigest = this.hashCodeForString(JSON.stringify(manifest)) } catch (error) { UpdaterLogger.error('Couldn\t parse text as json') manifest = {} manifestDigest = 0 } UpdaterLogger.debug('Manifest loaded', manifest, manifestDigest) return { manifest, manifestDigest } } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,7 +0,0 @@ -
romanlex revised this gist
Jan 30, 2023 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1 +1 @@ export const ASSET_MANIFEST = JSON.parse(DEFINE_PLUGIN_ASSET_MANIFEST) -
romanlex revised this gist
Jan 30, 2023 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -9,6 +9,7 @@ modules.exports = () => { fileName: path.resolve(paths.railsPublic, 'manifest.json'), filter: ({ name, isInitial, isChunk }) => isInitial || name.includes('.js') || isChunk, assetHookStage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS, writeToFileEmit: true, }), new BundleManifestPlugin({ to: 'DEFINE_PLUGIN_ASSET_MANIFEST', -
romanlex revised this gist
Jan 30, 2023 . 2 changed files with 1 addition and 10 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,15 +3,6 @@ const { Compilation } = require('webpack') modules.exports = () => { return { // ... plugin: [ // ... new WebpackManifestPlugin({ This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,6 +2,6 @@ import { ASSET_MANIFEST } from 'constants/webpack' const response = await fetch('/manifest.json') const currentManifest = await response.json() if(JSON.stringify(ASSET_MANIFEST) !== JSON.stringify(currentManifest)) { // your app is outdated } -
romanlex revised this gist
Jan 30, 2023 . 4 changed files with 34 additions and 17 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -37,6 +37,7 @@ const PLUGIN_NAME = 'BundleManifestPlugin' /** * @typedef {Object} Options * @property {string} to * @property {number} assetHookStage */ class BundleManifestPlugin { @@ -67,16 +68,17 @@ class BundleManifestPlugin { compilation.hooks.optimizeModules.tap(PLUGIN_NAME, (modules) => { for (const module of modules) { if ( !moduleForReplace && module._source && module._source._valueAsString && new RegExp(options.to, 'g').test(module._source._valueAsString) ) { moduleForReplace = module } } }) compilation.hooks.processAssets.tap({ name: PLUGIN_NAME, stage: options.assetHookStage ?? Infinity }, () => { if (!this.manifest) return for (const chunk of compilation.chunks) { This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,16 +1,28 @@ const { Compilation } = require('webpack') modules.exports = () => { return { // ... optimization: { // ... minimize: true, minimizer: [ new TerserPlugin({ exclude: /constants\/webpack/, }), ], }, plugin: [ // ... new WebpackManifestPlugin({ fileName: path.resolve(paths.railsPublic, 'manifest.json'), filter: ({ name, isInitial, isChunk }) => isInitial || name.includes('.js') || isChunk, assetHookStage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS, }), new BundleManifestPlugin({ to: 'DEFINE_PLUGIN_ASSET_MANIFEST', assetHookStage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS, }) ] } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1 @@ export const ASSET_MANIFEST = DEFINE_PLUGIN_ASSET_MANIFEST This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,7 @@ import { ASSET_MANIFEST } from 'constants/webpack' const response = await fetch('/manifest.json') const currentManifest = await response.json() if(JSON.stringify(currentManifest) !== JSON.stringify(ASSET_MANIFEST)) { // your app is outdated } -
romanlex revised this gist
Jan 30, 2023 . 1 changed file with 15 additions and 11 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,5 @@ const { getCompilerHooks } = require('webpack-manifest-plugin') const { sources } = require('webpack') const PLUGIN_NAME = 'BundleManifestPlugin' @@ -75,20 +76,23 @@ class BundleManifestPlugin { } }) compilation.hooks.processAssets.tap({ name: PLUGIN_NAME, stage: Infinity }, () => { if (!this.manifest) return for (const chunk of compilation.chunks) { if (!chunk.containsModule(moduleForReplace)) continue for (const file of chunk.files) { compilation.updateAsset(file, (oldSource) => { const source = oldSource.source() return new sources.RawSource(source.replace(options.to, JSON.stringify(this.manifest))) }) } } }) beforeEmit.tap(PLUGIN_NAME, (manifest) => { this.manifest = JSON.stringify(manifest) return manifest }) }) -
romanlex revised this gist
Jan 27, 2023 . 1 changed file with 30 additions and 19 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -58,29 +58,40 @@ class BundleManifestPlugin { */ apply(compiler) { const { beforeEmit } = getCompilerHooks(compiler) const { options } = this let moduleForReplace compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => { compilation.hooks.optimizeModules.tap(PLUGIN_NAME, (modules) => { for (const module of modules) { if ( module._source && module._source._valueAsString && new RegExp(options.to, 'gi').test(module._source._valueAsString) ) { moduleForReplace = module } } }) beforeEmit.tap(PLUGIN_NAME, (manifest) => { this.manifest = JSON.stringify(manifest) if (moduleForReplace && moduleForReplace._source._valueAsString) { const source = moduleForReplace._source if (new RegExp(options.to, 'gi').test(source._valueAsString)) { source._valueAsString = source._valueAsString.replace( new RegExp(options.to, 'gi'), JSON.stringify(manifest) ) moduleForReplace._source = source compilation.rebuildModule(moduleForReplace) } } return manifest }) }) } } -
romanlex renamed this gist
Jan 27, 2023 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
romanlex revised this gist
Jan 27, 2023 . 2 changed files with 21 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,5 @@ const response = await fetch('/manifest.json') const currentManifest = await response.json() if(JSON.stringify(currentManifest) !== JSON.stringify(DEFINE_PLUGIN_ASSET_MANIFEST)) { // your app is outdated } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,16 @@ modules.exports = () => { return { // ... plugin: [ // ... new WebpackManifestPlugin({ fileName: path.resolve(paths.railsPublic, 'manifest.json'), filter: ({ name, isInitial, isChunk }) => isInitial || name.includes('.js') || isChunk, }), new BundleManifestPlugin({ to: 'DEFINE_PLUGIN_ASSET_MANIFEST', }) ] } } -
romanlex created this gist
Jan 27, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,87 @@ const { getCompilerHooks } = require('webpack-manifest-plugin') const PLUGIN_NAME = 'BundleManifestPlugin' /** * Bundle asset manifest to defined var * For example you need check what your frontend app is outdated * You can make diff manifest.json with defined var at runtime for check this * * Example use: * ```js * plugin: [ * ... * new WebpackManifestPlugin({ * fileName: path.resolve(paths.railsPublic, 'manifest.json'), * filter: ({ name, isInitial, isChunk }) => isInitial || name.includes('.js') || isChunk, * }), * new BundleManifestPlugin({ * to: 'DEFINE_PLUGIN_ASSET_MANIFEST', * }) * ] * ``` * And now in your code: * ```js * const response = await fetch('/manifest.json') * const currentManifest = await response.json() * * if(JSON.stringify(currentManifest) !== JSON.stringify(DEFINE_PLUGIN_ASSET_MANIFEST)) { * // your app is outdated * } * ``` */ /** @typedef {import("webpack").Compiler} Compiler */ /** * @typedef {Object} Options * @property {string} to */ class BundleManifestPlugin { /** @type {Options} */ options manifest /** * * @param {Options} options */ constructor(options) { this.options = options } /** * Apply the plugin * @param {Compiler} compiler the compiler instance * @returns {void} */ apply(compiler) { const { beforeEmit } = getCompilerHooks(compiler) beforeEmit.tap(PLUGIN_NAME, (manifest) => { this.manifest = JSON.stringify(manifest) return manifest }) compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => { compilation.hooks.optimizeModules.tap(PLUGIN_NAME, (modules) => this.onOptimizeModules(modules)) }) } onOptimizeModules(modules) { const { options } = this for (const module of modules) { if ( options.to && module._source && module._source._value && new RegExp(options.to, 'gi').test(module._source._value) ) { module._source._value = module._source._value.replace(new RegExp(options.to, 'gi'), this.manifest ?? '{}') } } } } exports.BundleManifestPlugin = BundleManifestPlugin