Merge branch 'enable-webpack-code-splitting' into 'master'
Enable webpack code splitting Closes #32989 See merge request !12032
This commit is contained in:
commit
e48f54b5b2
15 changed files with 140 additions and 72 deletions
|
@ -11,6 +11,7 @@
|
|||
"gon": false,
|
||||
"localStorage": false
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"plugins": [
|
||||
"filenames",
|
||||
"import",
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
/* global Flash */
|
||||
|
||||
import Cookies from 'js-cookie';
|
||||
import * as Emoji from './emoji';
|
||||
|
||||
const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd';
|
||||
const transitionEndEventString = 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd';
|
||||
|
@ -24,27 +23,9 @@ const categoryLabelMap = {
|
|||
flags: 'Flags',
|
||||
};
|
||||
|
||||
function renderCategory(name, emojiList, opts = {}) {
|
||||
return `
|
||||
<h5 class="emoji-menu-title">
|
||||
${name}
|
||||
</h5>
|
||||
<ul class="clearfix emoji-menu-list ${opts.menuListClass || ''}">
|
||||
${emojiList.map(emojiName => `
|
||||
<li class="emoji-menu-list-item">
|
||||
<button class="emoji-menu-btn text-center js-emoji-btn" type="button">
|
||||
${Emoji.glEmojiTag(emojiName, {
|
||||
sprite: true,
|
||||
})}
|
||||
</button>
|
||||
</li>
|
||||
`).join('\n')}
|
||||
</ul>
|
||||
`;
|
||||
}
|
||||
|
||||
export default class AwardsHandler {
|
||||
constructor() {
|
||||
class AwardsHandler {
|
||||
constructor(emoji) {
|
||||
this.emoji = emoji;
|
||||
this.eventListeners = [];
|
||||
// If the user shows intent let's pre-build the menu
|
||||
this.registerEventListener('one', $(document), 'mouseenter focus', '.js-add-award', 'mouseenter focus', () => {
|
||||
|
@ -78,10 +59,10 @@ export default class AwardsHandler {
|
|||
const $target = $(e.currentTarget);
|
||||
const $glEmojiElement = $target.find('gl-emoji');
|
||||
const $spriteIconElement = $target.find('.icon');
|
||||
const emoji = ($glEmojiElement.length ? $glEmojiElement : $spriteIconElement).data('name');
|
||||
const emojiName = ($glEmojiElement.length ? $glEmojiElement : $spriteIconElement).data('name');
|
||||
|
||||
$target.closest('.js-awards-block').addClass('current');
|
||||
this.addAward(this.getVotesBlock(), this.getAwardUrl(), emoji);
|
||||
this.addAward(this.getVotesBlock(), this.getAwardUrl(), emojiName);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -139,16 +120,16 @@ export default class AwardsHandler {
|
|||
this.isCreatingEmojiMenu = true;
|
||||
|
||||
// Render the first category
|
||||
const categoryMap = Emoji.getEmojiCategoryMap();
|
||||
const categoryMap = this.emoji.getEmojiCategoryMap();
|
||||
const categoryNameKey = Object.keys(categoryMap)[0];
|
||||
const emojisInCategory = categoryMap[categoryNameKey];
|
||||
const firstCategory = renderCategory(categoryLabelMap[categoryNameKey], emojisInCategory);
|
||||
const firstCategory = this.renderCategory(categoryLabelMap[categoryNameKey], emojisInCategory);
|
||||
|
||||
// Render the frequently used
|
||||
const frequentlyUsedEmojis = this.getFrequentlyUsedEmojis();
|
||||
let frequentlyUsedCatgegory = '';
|
||||
if (frequentlyUsedEmojis.length > 0) {
|
||||
frequentlyUsedCatgegory = renderCategory('Frequently used', frequentlyUsedEmojis, {
|
||||
frequentlyUsedCatgegory = this.renderCategory('Frequently used', frequentlyUsedEmojis, {
|
||||
menuListClass: 'frequent-emojis',
|
||||
});
|
||||
}
|
||||
|
@ -179,7 +160,7 @@ export default class AwardsHandler {
|
|||
}
|
||||
this.isAddingRemainingEmojiMenuCategories = true;
|
||||
|
||||
const categoryMap = Emoji.getEmojiCategoryMap();
|
||||
const categoryMap = this.emoji.getEmojiCategoryMap();
|
||||
|
||||
// Avoid the jank and render the remaining categories separately
|
||||
// This will take more time, but makes UI more responsive
|
||||
|
@ -191,7 +172,7 @@ export default class AwardsHandler {
|
|||
promiseChain.then(() =>
|
||||
new Promise((resolve) => {
|
||||
const emojisInCategory = categoryMap[categoryNameKey];
|
||||
const categoryMarkup = renderCategory(
|
||||
const categoryMarkup = this.renderCategory(
|
||||
categoryLabelMap[categoryNameKey],
|
||||
emojisInCategory,
|
||||
);
|
||||
|
@ -216,6 +197,25 @@ export default class AwardsHandler {
|
|||
});
|
||||
}
|
||||
|
||||
renderCategory(name, emojiList, opts = {}) {
|
||||
return `
|
||||
<h5 class="emoji-menu-title">
|
||||
${name}
|
||||
</h5>
|
||||
<ul class="clearfix emoji-menu-list ${opts.menuListClass || ''}">
|
||||
${emojiList.map(emojiName => `
|
||||
<li class="emoji-menu-list-item">
|
||||
<button class="emoji-menu-btn text-center js-emoji-btn" type="button">
|
||||
${this.emoji.glEmojiTag(emojiName, {
|
||||
sprite: true,
|
||||
})}
|
||||
</button>
|
||||
</li>
|
||||
`).join('\n')}
|
||||
</ul>
|
||||
`;
|
||||
}
|
||||
|
||||
positionMenu($menu, $addBtn) {
|
||||
const position = $addBtn.data('position');
|
||||
// The menu could potentially be off-screen or in a hidden overflow element
|
||||
|
@ -234,7 +234,7 @@ export default class AwardsHandler {
|
|||
}
|
||||
|
||||
addAward(votesBlock, awardUrl, emoji, checkMutuality, callback) {
|
||||
const normalizedEmoji = Emoji.normalizeEmojiName(emoji);
|
||||
const normalizedEmoji = this.emoji.normalizeEmojiName(emoji);
|
||||
const $emojiButton = this.findEmojiIcon(votesBlock, normalizedEmoji).parent();
|
||||
this.postEmoji($emojiButton, awardUrl, normalizedEmoji, () => {
|
||||
this.addAwardToEmojiBar(votesBlock, normalizedEmoji, checkMutuality);
|
||||
|
@ -249,7 +249,7 @@ export default class AwardsHandler {
|
|||
this.checkMutuality(votesBlock, emoji);
|
||||
}
|
||||
this.addEmojiToFrequentlyUsedList(emoji);
|
||||
const normalizedEmoji = Emoji.normalizeEmojiName(emoji);
|
||||
const normalizedEmoji = this.emoji.normalizeEmojiName(emoji);
|
||||
const $emojiButton = this.findEmojiIcon(votesBlock, normalizedEmoji).parent();
|
||||
if ($emojiButton.length > 0) {
|
||||
if (this.isActive($emojiButton)) {
|
||||
|
@ -374,7 +374,7 @@ export default class AwardsHandler {
|
|||
createAwardButtonForVotesBlock(votesBlock, emojiName) {
|
||||
const buttonHtml = `
|
||||
<button class="btn award-control js-emoji-btn has-tooltip active" title="You" data-placement="bottom">
|
||||
${Emoji.glEmojiTag(emojiName)}
|
||||
${this.emoji.glEmojiTag(emojiName)}
|
||||
<span class="award-control-text js-counter">1</span>
|
||||
</button>
|
||||
`;
|
||||
|
@ -440,7 +440,7 @@ export default class AwardsHandler {
|
|||
}
|
||||
|
||||
addEmojiToFrequentlyUsedList(emoji) {
|
||||
if (Emoji.isEmojiNameValid(emoji)) {
|
||||
if (this.emoji.isEmojiNameValid(emoji)) {
|
||||
this.frequentlyUsedEmojis = _.uniq(this.getFrequentlyUsedEmojis().concat(emoji));
|
||||
Cookies.set('frequently_used_emojis', this.frequentlyUsedEmojis.join(','), { expires: 365 });
|
||||
}
|
||||
|
@ -450,7 +450,7 @@ export default class AwardsHandler {
|
|||
return this.frequentlyUsedEmojis || (() => {
|
||||
const frequentlyUsedEmojis = _.uniq((Cookies.get('frequently_used_emojis') || '').split(','));
|
||||
this.frequentlyUsedEmojis = frequentlyUsedEmojis.filter(
|
||||
inputName => Emoji.isEmojiNameValid(inputName),
|
||||
inputName => this.emoji.isEmojiNameValid(inputName),
|
||||
);
|
||||
|
||||
return this.frequentlyUsedEmojis;
|
||||
|
@ -493,7 +493,7 @@ export default class AwardsHandler {
|
|||
}
|
||||
|
||||
findMatchingEmojiElements(query) {
|
||||
const emojiMatches = Emoji.filterEmojiNamesByAlias(query);
|
||||
const emojiMatches = this.emoji.filterEmojiNamesByAlias(query);
|
||||
const $emojiElements = $('.emoji-menu-list:not(.frequent-emojis) [data-name]');
|
||||
const $matchingElements = $emojiElements
|
||||
.filter((i, elm) => emojiMatches.indexOf(elm.dataset.name) >= 0);
|
||||
|
@ -507,3 +507,12 @@ export default class AwardsHandler {
|
|||
$('.emoji-menu').remove();
|
||||
}
|
||||
}
|
||||
|
||||
let awardsHandlerPromise = null;
|
||||
export default function loadAwardsHandler(reload = false) {
|
||||
if (!awardsHandlerPromise || reload) {
|
||||
awardsHandlerPromise = import(/* webpackChunkName: 'emoji' */ './emoji')
|
||||
.then(Emoji => new AwardsHandler(Emoji));
|
||||
}
|
||||
return awardsHandlerPromise;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import installCustomElements from 'document-register-element';
|
||||
import { emojiImageTag, emojiFallbackImageSrc } from '../emoji';
|
||||
import isEmojiUnicodeSupported from '../emoji/support';
|
||||
|
||||
installCustomElements(window);
|
||||
|
@ -32,11 +31,19 @@ export default function installGlEmojiElement() {
|
|||
// IE 11 doesn't like adding multiple at once :(
|
||||
this.classList.add('emoji-icon');
|
||||
this.classList.add(fallbackSpriteClass);
|
||||
} else if (hasImageFallback) {
|
||||
this.innerHTML = emojiImageTag(name, fallbackSrc);
|
||||
} else {
|
||||
const src = emojiFallbackImageSrc(name);
|
||||
this.innerHTML = emojiImageTag(name, src);
|
||||
import(/* webpackChunkName: 'emoji' */ '../emoji')
|
||||
.then(({ emojiImageTag, emojiFallbackImageSrc }) => {
|
||||
if (hasImageFallback) {
|
||||
this.innerHTML = emojiImageTag(name, fallbackSrc);
|
||||
} else {
|
||||
const src = emojiFallbackImageSrc(name);
|
||||
this.innerHTML = emojiImageTag(name, src);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// do nothing
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { validEmojiNames, glEmojiTag } from './emoji';
|
||||
import glRegexp from './lib/utils/regexp';
|
||||
import AjaxCache from './lib/utils/ajax_cache';
|
||||
|
||||
|
@ -373,7 +372,12 @@ class GfmAutoComplete {
|
|||
if (this.cachedData[at]) {
|
||||
this.loadData($input, at, this.cachedData[at]);
|
||||
} else if (GfmAutoComplete.atTypeMap[at] === 'emojis') {
|
||||
this.loadData($input, at, validEmojiNames);
|
||||
import(/* webpackChunkName: 'emoji' */ './emoji')
|
||||
.then(({ validEmojiNames, glEmojiTag }) => {
|
||||
this.loadData($input, at, validEmojiNames);
|
||||
GfmAutoComplete.glEmojiTag = glEmojiTag;
|
||||
})
|
||||
.catch(() => { this.isLoadingData[at] = false; });
|
||||
} else {
|
||||
AjaxCache.retrieve(this.dataSources[GfmAutoComplete.atTypeMap[at]], true)
|
||||
.then((data) => {
|
||||
|
@ -428,12 +432,14 @@ GfmAutoComplete.atTypeMap = {
|
|||
};
|
||||
|
||||
// Emoji
|
||||
GfmAutoComplete.glEmojiTag = null;
|
||||
GfmAutoComplete.Emoji = {
|
||||
templateFunction(name) {
|
||||
return `<li>
|
||||
${name} ${glEmojiTag(name)}
|
||||
</li>
|
||||
`;
|
||||
// glEmojiTag helper is loaded on-demand in fetchData()
|
||||
if (GfmAutoComplete.glEmojiTag) {
|
||||
return `<li>${name} ${GfmAutoComplete.glEmojiTag(name)}</li>`;
|
||||
}
|
||||
return `<li>${name}</li>`;
|
||||
},
|
||||
};
|
||||
// Team Members
|
||||
|
|
|
@ -70,7 +70,7 @@ import './ajax_loading_spinner';
|
|||
import './api';
|
||||
import './aside';
|
||||
import './autosave';
|
||||
import AwardsHandler from './awards_handler';
|
||||
import loadAwardsHandler from './awards_handler';
|
||||
import './breakpoints';
|
||||
import './broadcast_message';
|
||||
import './build';
|
||||
|
@ -355,7 +355,7 @@ $(function () {
|
|||
$window.off('resize.app').on('resize.app', function () {
|
||||
return fitSidebarForSize();
|
||||
});
|
||||
gl.awardsHandler = new AwardsHandler();
|
||||
loadAwardsHandler();
|
||||
new Aside();
|
||||
|
||||
gl.utils.renderTimeago();
|
||||
|
|
|
@ -18,6 +18,7 @@ import 'vendor/jquery.caret'; // required by jquery.atwho
|
|||
import 'vendor/jquery.atwho';
|
||||
import AjaxCache from '~/lib/utils/ajax_cache';
|
||||
import CommentTypeToggle from './comment_type_toggle';
|
||||
import loadAwardsHandler from './awards_handler';
|
||||
import './autosave';
|
||||
import './dropzone_input';
|
||||
import './task_list';
|
||||
|
@ -291,8 +292,13 @@ export default class Notes {
|
|||
|
||||
if ('emoji_award' in noteEntity.commands_changes) {
|
||||
votesBlock = $('.js-awards-block').eq(0);
|
||||
gl.awardsHandler.addAwardToEmojiBar(votesBlock, noteEntity.commands_changes.emoji_award);
|
||||
return gl.awardsHandler.scrollToAwards();
|
||||
|
||||
loadAwardsHandler().then((awardsHandler) => {
|
||||
awardsHandler.addAwardToEmojiBar(votesBlock, noteEntity.commands_changes.emoji_award);
|
||||
awardsHandler.scrollToAwards();
|
||||
}).catch(() => {
|
||||
// ignore
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
9
app/assets/javascripts/webpack.js
Normal file
9
app/assets/javascripts/webpack.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* This is the first script loaded by webpack's runtime. It is used to manually configure
|
||||
* config.output.publicPath to account for relative_url_root or CDN settings which cannot be
|
||||
* baked-in to our webpack bundles.
|
||||
*/
|
||||
|
||||
if (gon && gon.webpack_public_path) {
|
||||
__webpack_public_path__ = gon.webpack_public_path; // eslint-disable-line
|
||||
}
|
|
@ -11,20 +11,29 @@ module WebpackHelper
|
|||
|
||||
paths = Webpack::Rails::Manifest.asset_paths(source)
|
||||
if extension
|
||||
paths = paths.select { |p| p.ends_with? ".#{extension}" }
|
||||
paths.select! { |p| p.ends_with? ".#{extension}" }
|
||||
end
|
||||
|
||||
# include full webpack-dev-server url for rspec tests running locally
|
||||
if Rails.env.test? && Rails.configuration.webpack.dev_server.enabled
|
||||
host = Rails.configuration.webpack.dev_server.host
|
||||
port = Rails.configuration.webpack.dev_server.port
|
||||
protocol = Rails.configuration.webpack.dev_server.https ? 'https' : 'http'
|
||||
|
||||
paths.map! do |p|
|
||||
"#{protocol}://#{host}:#{port}#{p}"
|
||||
end
|
||||
force_host = webpack_public_host
|
||||
if force_host
|
||||
paths.map! { |p| "#{force_host}#{p}" }
|
||||
end
|
||||
|
||||
paths
|
||||
end
|
||||
|
||||
def webpack_public_host
|
||||
if Rails.env.test? && Rails.configuration.webpack.dev_server.enabled
|
||||
host = Rails.configuration.webpack.dev_server.host
|
||||
port = Rails.configuration.webpack.dev_server.port
|
||||
protocol = Rails.configuration.webpack.dev_server.https ? 'https' : 'http'
|
||||
"#{protocol}://#{host}:#{port}"
|
||||
else
|
||||
ActionController::Base.asset_host.try(:chomp, '/')
|
||||
end
|
||||
end
|
||||
|
||||
def webpack_public_path
|
||||
"#{webpack_public_host}/#{Rails.application.config.webpack.public_path}/"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
= Gon::Base.render_data
|
||||
|
||||
= webpack_bundle_tag "runtime"
|
||||
= webpack_bundle_tag "webpack_runtime"
|
||||
= webpack_bundle_tag "common"
|
||||
= webpack_bundle_tag "locale"
|
||||
= webpack_bundle_tag "main"
|
||||
|
|
5
changelogs/unreleased/enable-webpack-code-splitting.yml
Normal file
5
changelogs/unreleased/enable-webpack-code-splitting.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Enable support for webpack code-splitting by dynamically setting publicPath
|
||||
at runtime
|
||||
merge_request: 12032
|
||||
author:
|
|
@ -71,6 +71,7 @@ var config = {
|
|||
vue_merge_request_widget: './vue_merge_request_widget/index.js',
|
||||
test: './test.js',
|
||||
peek: './peek.js',
|
||||
webpack_runtime: './webpack.js',
|
||||
},
|
||||
|
||||
output: {
|
||||
|
@ -190,7 +191,7 @@ var config = {
|
|||
|
||||
// create cacheable common library bundles
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
names: ['main', 'locale', 'common', 'runtime'],
|
||||
names: ['main', 'locale', 'common', 'webpack_runtime'],
|
||||
}),
|
||||
],
|
||||
|
||||
|
@ -245,7 +246,6 @@ if (IS_DEV_SERVER) {
|
|||
hot: DEV_SERVER_LIVERELOAD,
|
||||
inline: DEV_SERVER_LIVERELOAD
|
||||
};
|
||||
config.output.publicPath = '//' + DEV_SERVER_HOST + ':' + DEV_SERVER_PORT + config.output.publicPath;
|
||||
config.plugins.push(
|
||||
// watch node_modules for changes if we encounter a missing module compile error
|
||||
new WatchMissingNodeModulesPlugin(path.join(ROOT_PATH, 'node_modules'))
|
||||
|
|
|
@ -2,11 +2,14 @@
|
|||
|
||||
module Gitlab
|
||||
module GonHelper
|
||||
include WebpackHelper
|
||||
|
||||
def add_gon_variables
|
||||
gon.api_version = 'v4'
|
||||
gon.default_avatar_url = URI.join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s
|
||||
gon.max_file_size = current_application_settings.max_attachment_size
|
||||
gon.asset_host = ActionController::Base.asset_host
|
||||
gon.webpack_public_path = webpack_public_path
|
||||
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
|
||||
gon.shortcuts_path = help_page_path('shortcuts')
|
||||
gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"babel-core": "^6.22.1",
|
||||
"babel-eslint": "^7.2.1",
|
||||
"babel-loader": "^6.2.10",
|
||||
"babel-plugin-transform-define": "^1.2.0",
|
||||
"babel-preset-latest": "^6.24.0",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, no-unused-expressions, comma-dangle, new-parens, no-unused-vars, quotes, jasmine/no-spec-dupes, prefer-template, max-len */
|
||||
|
||||
import Cookies from 'js-cookie';
|
||||
import AwardsHandler from '~/awards_handler';
|
||||
import loadAwardsHandler from '~/awards_handler';
|
||||
|
||||
import '~/lib/utils/common_utils';
|
||||
|
||||
|
@ -26,14 +26,13 @@ import '~/lib/utils/common_utils';
|
|||
|
||||
describe('AwardsHandler', function() {
|
||||
preloadFixtures('issues/issue_with_comment.html.raw');
|
||||
beforeEach(function() {
|
||||
beforeEach(function(done) {
|
||||
loadFixtures('issues/issue_with_comment.html.raw');
|
||||
awardsHandler = new AwardsHandler;
|
||||
spyOn(awardsHandler, 'postEmoji').and.callFake((function(_this) {
|
||||
return function(button, url, emoji, cb) {
|
||||
return cb();
|
||||
};
|
||||
})(this));
|
||||
loadAwardsHandler(true).then((obj) => {
|
||||
awardsHandler = obj;
|
||||
spyOn(awardsHandler, 'postEmoji').and.callFake((button, url, emoji, cb) => cb());
|
||||
done();
|
||||
}).catch(fail);
|
||||
|
||||
let isEmojiMenuBuilt = false;
|
||||
openAndWaitForEmojiMenu = function() {
|
||||
|
|
15
yarn.lock
15
yarn.lock
|
@ -265,6 +265,15 @@ babel-core@^6.22.1, babel-core@^6.23.0:
|
|||
slash "^1.0.0"
|
||||
source-map "^0.5.0"
|
||||
|
||||
babel-eslint@^7.2.1:
|
||||
version "7.2.1"
|
||||
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.2.1.tgz#079422eb73ba811e3ca0865ce87af29327f8c52f"
|
||||
dependencies:
|
||||
babel-code-frame "^6.22.0"
|
||||
babel-traverse "^6.23.1"
|
||||
babel-types "^6.23.0"
|
||||
babylon "^6.16.1"
|
||||
|
||||
babel-generator@^6.18.0, babel-generator@^6.23.0:
|
||||
version "6.23.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.23.0.tgz#6b8edab956ef3116f79d8c84c5a3c05f32a74bc5"
|
||||
|
@ -816,10 +825,14 @@ babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.22.0, babel-types@^6.23
|
|||
lodash "^4.2.0"
|
||||
to-fast-properties "^1.0.1"
|
||||
|
||||
babylon@^6.11.0, babylon@^6.13.0, babylon@^6.15.0:
|
||||
babylon@^6.11.0:
|
||||
version "6.15.0"
|
||||
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.15.0.tgz#ba65cfa1a80e1759b0e89fb562e27dccae70348e"
|
||||
|
||||
babylon@^6.13.0, babylon@^6.15.0, babylon@^6.16.1:
|
||||
version "6.16.1"
|
||||
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.16.1.tgz#30c5a22f481978a9e7f8cdfdf496b11d94b404d3"
|
||||
|
||||
backo2@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
|
||||
|
|
Loading…
Reference in a new issue