Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/lib/utils/vue3compat')
-rw-r--r--app/assets/javascripts/lib/utils/vue3compat/compat_functional_mixin.js14
-rw-r--r--app/assets/javascripts/lib/utils/vue3compat/get_instance_from_directive.js9
-rw-r--r--app/assets/javascripts/lib/utils/vue3compat/mark_raw.js9
-rw-r--r--app/assets/javascripts/lib/utils/vue3compat/normalize_children.js11
-rw-r--r--app/assets/javascripts/lib/utils/vue3compat/vue_apollo.js78
-rw-r--r--app/assets/javascripts/lib/utils/vue3compat/vue_router.js115
-rw-r--r--app/assets/javascripts/lib/utils/vue3compat/vuex.js38
7 files changed, 274 insertions, 0 deletions
diff --git a/app/assets/javascripts/lib/utils/vue3compat/compat_functional_mixin.js b/app/assets/javascripts/lib/utils/vue3compat/compat_functional_mixin.js
new file mode 100644
index 00000000000..ec8feb7d2e6
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/vue3compat/compat_functional_mixin.js
@@ -0,0 +1,14 @@
+import Vue from 'vue';
+
+export const compatFunctionalMixin = Vue.version.startsWith('3')
+ ? {
+ created() {
+ this.props = this.$props;
+ this.listeners = this.$listeners;
+ },
+ }
+ : {
+ created() {
+ throw new Error('This mixin should not be executed in Vue.js 2');
+ },
+ };
diff --git a/app/assets/javascripts/lib/utils/vue3compat/get_instance_from_directive.js b/app/assets/javascripts/lib/utils/vue3compat/get_instance_from_directive.js
new file mode 100644
index 00000000000..b69f5e0c546
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/vue3compat/get_instance_from_directive.js
@@ -0,0 +1,9 @@
+// See https://v3-migration.vuejs.org/breaking-changes/custom-directives.html#edge-case-accessing-the-component-instance
+export function getInstanceFromDirective({ binding, vnode }) {
+ if (binding.instance) {
+ // this is Vue.js 3, even in compat mode
+ return binding.instance;
+ }
+
+ return vnode.context;
+}
diff --git a/app/assets/javascripts/lib/utils/vue3compat/mark_raw.js b/app/assets/javascripts/lib/utils/vue3compat/mark_raw.js
new file mode 100644
index 00000000000..daafbad8ba1
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/vue3compat/mark_raw.js
@@ -0,0 +1,9 @@
+// this will be replaced by markRaw from vue.js v3
+export function markRaw(obj) {
+ Object.defineProperty(obj, '__v_skip', {
+ value: true,
+ configurable: true,
+ });
+
+ return obj;
+}
diff --git a/app/assets/javascripts/lib/utils/vue3compat/normalize_children.js b/app/assets/javascripts/lib/utils/vue3compat/normalize_children.js
new file mode 100644
index 00000000000..616bd4786a9
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/vue3compat/normalize_children.js
@@ -0,0 +1,11 @@
+export function normalizeChildren(children) {
+ if (typeof children !== 'object' || Array.isArray(children)) {
+ return children;
+ }
+
+ if (typeof children.default === 'function') {
+ return children.default();
+ }
+
+ return children;
+}
diff --git a/app/assets/javascripts/lib/utils/vue3compat/vue_apollo.js b/app/assets/javascripts/lib/utils/vue3compat/vue_apollo.js
new file mode 100644
index 00000000000..fd08d34a80e
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/vue3compat/vue_apollo.js
@@ -0,0 +1,78 @@
+import Vue from 'vue';
+import { createApolloProvider } from '@vue/apollo-option';
+import { ApolloMutation } from '@vue/apollo-components';
+
+export { ApolloMutation };
+
+const installed = new WeakMap();
+
+function callLifecycle(hookName, ...extraArgs) {
+ const { GITLAB_INTERNAL_ADDED_MIXINS: addedMixins } = this.$;
+ if (!addedMixins) {
+ return [];
+ }
+
+ return addedMixins.map((m) => m[hookName]?.apply(this, extraArgs));
+}
+
+function createMixinForLateInit({ install, shouldInstall }) {
+ return {
+ created() {
+ callLifecycle.call(this, 'created');
+ },
+ // @vue/compat normalizez lifecycle hook names so there is no error here
+ destroyed() {
+ callLifecycle.call(this, 'unmounted');
+ },
+
+ data(...args) {
+ const extraData = callLifecycle.call(this, 'data', ...args);
+ if (!extraData.length) {
+ return {};
+ }
+
+ return Object.assign({}, ...extraData);
+ },
+
+ beforeCreate() {
+ if (shouldInstall(this)) {
+ const { mixins } = this.$.appContext;
+ const globalMixinsBeforeInit = new Set(mixins);
+ install(this);
+
+ this.$.GITLAB_INTERNAL_ADDED_MIXINS = mixins.filter((m) => !globalMixinsBeforeInit.has(m));
+
+ callLifecycle.call(this, 'beforeCreate');
+ }
+ },
+ };
+}
+
+export default class VueCompatApollo {
+ constructor(...args) {
+ // eslint-disable-next-line no-constructor-return
+ return createApolloProvider(...args);
+ }
+
+ static install() {
+ Vue.mixin(
+ createMixinForLateInit({
+ shouldInstall: (vm) =>
+ vm.$options.apolloProvider &&
+ !installed.get(vm.$.appContext.app)?.has(vm.$options.apolloProvider),
+ install: (vm) => {
+ const { app } = vm.$.appContext;
+ const { apolloProvider } = vm.$options;
+
+ if (!installed.has(app)) {
+ installed.set(app, new WeakSet());
+ }
+
+ installed.get(app).add(apolloProvider);
+
+ vm.$.appContext.app.use(vm.$options.apolloProvider);
+ },
+ }),
+ );
+ }
+}
diff --git a/app/assets/javascripts/lib/utils/vue3compat/vue_router.js b/app/assets/javascripts/lib/utils/vue3compat/vue_router.js
new file mode 100644
index 00000000000..aa2963ece31
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/vue3compat/vue_router.js
@@ -0,0 +1,115 @@
+import Vue from 'vue';
+import {
+ createRouter,
+ createMemoryHistory,
+ createWebHistory,
+ createWebHashHistory,
+} from 'vue-router-vue3';
+
+const mode = (value, options) => {
+ if (!value) return null;
+ let history;
+ // eslint-disable-next-line default-case
+ switch (value) {
+ case 'history':
+ history = createWebHistory(options.base);
+ break;
+ case 'hash':
+ history = createWebHashHistory();
+ break;
+ case 'abstract':
+ history = createMemoryHistory();
+ break;
+ }
+ return { history };
+};
+
+const base = () => null;
+
+const toNewCatchAllPath = (path) => {
+ if (path === '*') return '/:pathMatch(.*)*';
+ return path;
+};
+
+const routes = (value) => {
+ if (!value) return null;
+ const newRoutes = value.reduce(function handleRoutes(acc, route) {
+ const newRoute = {
+ ...route,
+ path: toNewCatchAllPath(route.path),
+ };
+ if (route.children) {
+ newRoute.children = route.children.reduce(handleRoutes, []);
+ }
+ acc.push(newRoute);
+ return acc;
+ }, []);
+ return { routes: newRoutes };
+};
+
+const scrollBehavior = (value) => {
+ return {
+ scrollBehavior(...args) {
+ const { x, y, left, top } = value(...args);
+ return { left: x || left, top: y || top };
+ },
+ };
+};
+
+const transformers = {
+ mode,
+ base,
+ routes,
+ scrollBehavior,
+};
+
+const transformOptions = (options = {}) => {
+ const defaultConfig = {
+ routes: [],
+ history: createWebHashHistory(),
+ };
+ return Object.keys(options).reduce((acc, key) => {
+ const value = options[key];
+ if (key in transformers) {
+ Object.assign(acc, transformers[key](value, options));
+ } else {
+ acc[key] = value;
+ }
+ return acc;
+ }, defaultConfig);
+};
+
+const installed = new WeakMap();
+
+export default class VueRouterCompat {
+ constructor(options) {
+ // eslint-disable-next-line no-constructor-return
+ return new Proxy(createRouter(transformOptions(options)), {
+ get(target, prop) {
+ const result = target[prop];
+ // eslint-disable-next-line no-underscore-dangle
+ if (result?.__v_isRef) {
+ return result.value;
+ }
+
+ return result;
+ },
+ });
+ }
+
+ static install() {
+ Vue.mixin({
+ beforeCreate() {
+ const { app } = this.$.appContext;
+ const { router } = this.$options;
+ if (router && !installed.get(app)?.has(router)) {
+ if (!installed.has(app)) {
+ installed.set(app, new WeakSet());
+ }
+ installed.get(app).add(router);
+ this.$.appContext.app.use(this.$options.router);
+ }
+ },
+ });
+ }
+}
diff --git a/app/assets/javascripts/lib/utils/vue3compat/vuex.js b/app/assets/javascripts/lib/utils/vue3compat/vuex.js
new file mode 100644
index 00000000000..ff94ff3d04a
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/vue3compat/vuex.js
@@ -0,0 +1,38 @@
+import Vue from 'vue';
+import {
+ createStore,
+ mapState,
+ mapGetters,
+ mapActions,
+ mapMutations,
+ createNamespacedHelpers,
+} from 'vuex-vue3';
+
+export { mapState, mapGetters, mapActions, mapMutations, createNamespacedHelpers };
+
+const installedStores = new WeakMap();
+
+export default {
+ Store: class VuexCompatStore {
+ constructor(...args) {
+ // eslint-disable-next-line no-constructor-return
+ return createStore(...args);
+ }
+ },
+
+ install() {
+ Vue.mixin({
+ beforeCreate() {
+ const { app } = this.$.appContext;
+ const { store } = this.$options;
+ if (store && !installedStores.get(app)?.has(store)) {
+ if (!installedStores.has(app)) {
+ installedStores.set(app, new WeakSet());
+ }
+ installedStores.get(app).add(store);
+ this.$.appContext.app.use(this.$options.store);
+ }
+ },
+ });
+ },
+};