Vue migration - remove Vuew.set & this.$set; add vue-migrate script

Signed-off-by: Francesco Torchia <francesco.torchia@suse.com>
This commit is contained in:
Francesco Torchia 2024-06-04 10:55:30 +02:00
parent c983ed8384
commit ea12a81174
No known key found for this signature in database
GPG Key ID: E6D011B7415D4393
85 changed files with 938 additions and 256 deletions

View File

@ -13,25 +13,27 @@ module.exports = {
],
// add your custom rules here
rules: {
'dot-notation': 'off',
'generator-star-spacing': 'off',
'guard-for-in': 'off',
'linebreak-style': 'off',
'new-cap': 'off',
'no-empty': 'off',
'no-extra-boolean-cast': 'off',
'no-new': 'off',
'no-plusplus': 'off',
'no-useless-escape': 'off',
'nuxt/no-cjs-in-config': 'off',
'semi-spacing': 'off',
'space-in-parens': 'off',
strict: 'off',
'unicorn/no-new-buffer': 'off',
'vue/html-self-closing': 'off',
'vue/no-unused-components': 'warn',
'vue/no-v-html': 'error',
'wrap-iife': 'off',
'dot-notation': 'off',
'generator-star-spacing': 'off',
'guard-for-in': 'off',
'linebreak-style': 'off',
'new-cap': 'off',
'no-empty': 'off',
'no-extra-boolean-cast': 'off',
'no-new': 'off',
'no-plusplus': 'off',
'no-useless-escape': 'off',
'nuxt/no-cjs-in-config': 'off',
'semi-spacing': 'off',
'space-in-parens': 'off',
strict: 'off',
'unicorn/no-new-buffer': 'off',
'vue/html-self-closing': 'off',
'vue/multi-word-component-names': 'off',
'vue/no-reserved-component-names': 'off',
'vue/no-unused-components': 'warn',
'vue/no-v-html': 'error',
'wrap-iife': 'off',
'array-bracket-spacing': 'warn',
'arrow-parens': 'warn',

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
dev
lint
# compiled output
/dist

View File

@ -22,6 +22,7 @@
"clean": "./node_modules/@rancher/shell/scripts/clean",
"build-pkg": "./node_modules/@rancher/shell/scripts/build-pkg.sh",
"serve-pkgs": "./node_modules/@rancher/shell/scripts/serve-pkgs",
"vue-migrate": "scripts/vue-migrate.js",
"publish-pkgs": "./node_modules/@rancher/shell/scripts/extension/publish",
"parse-tag-name": "./node_modules/@rancher/shell/scripts/extension/parse-tag-name",
"lint": "./node_modules/.bin/eslint --max-warnings 0 --ext .js,.ts,.vue ."

View File

@ -3,7 +3,7 @@ import { _VIEW, _EDIT, _CREATE } from '@shell/config/query-params';
import Tag from '@shell/components/Tag';
export default {
name: 'Tags',
name: 'DiskTags',
components: { Tag },

View File

@ -58,7 +58,7 @@ export default {
},
removeAll() {
this.$set(this, 'searchLabels', []);
this['searchLabels'] = [];
this.filterRows();
},

View File

@ -73,7 +73,7 @@ export default {
upgradeMessage = currentResource ? currentResource.upgradeMessage : [];
this.$set(this, 'upgradeMessage', upgradeMessage);
this['upgradeMessage'] = upgradeMessage;
},
deep: true
},

View File

@ -157,7 +157,7 @@ export default {
const preference = this.$store.getters['management/all'](STEVE.PREFERENCE)?.[0];
try {
this.$set(preference.data, PREFERED_SHORTCUT_KEYS, JSON.stringify(out));
preference.data[PREFERED_SHORTCUT_KEYS] = JSON.stringify(out);
await preference.save();
this.closeRecordingModal();
buttonCb(true);

View File

@ -143,6 +143,7 @@ export default {
let out = [];
if (!preference?.[0]?.data) {
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
this.hideCustomKeysBar = true;
return out;

View File

@ -30,7 +30,7 @@ export default {
update() {
const value = JSON.stringify(this.parseDefaultValue);
this.$set(this.value, 'value', value);
this.value['value'] = value;
}
},
@ -39,7 +39,7 @@ export default {
handler(neu) {
const parseDefaultValue = JSON.parse(neu.value);
this.$set(this, 'parseDefaultValue', parseDefaultValue);
this['parseDefaultValue'] = parseDefaultValue;
},
deep: true
}

View File

@ -80,7 +80,7 @@ export default {
parseDefaultValue = { type: '', endpoint: '' };
}
this.$set(this, 'parseDefaultValue', parseDefaultValue);
this['parseDefaultValue'] = parseDefaultValue;
this.update();
},
deep: true
@ -105,16 +105,16 @@ export default {
const value = JSON.stringify(this.parseDefaultValue);
if (!this.parseDefaultValue.type) {
this.$delete(this.value, 'value');
delete this.value['value'];
} else {
this.$set(this.value, 'value', value);
this.value['value'] = value;
}
},
useDefault() {
const parseDefaultValue = { type: '', endpoint: '' };
this.$set(this, 'parseDefaultValue', parseDefaultValue);
this['parseDefaultValue'] = parseDefaultValue;
}
}
};

View File

@ -183,7 +183,7 @@ export default {
const value = Object.keys(out).length ? JSON.stringify(out) : '';
this.$set(this.value, 'value', value);
this.value['value'] = value;
},
addMirror() {
@ -210,8 +210,8 @@ export default {
value: {
handler(value) {
if (!value.value) { // useDefaultVale
this.$set(this, 'mirrors', []);
this.$set(this, 'configs', []);
this['mirrors'] = [];
this['configs'] = [];
this.update();
}
},

View File

@ -122,7 +122,7 @@ export default {
const value = this.configArr.length ? JSON.stringify(out) : '';
this.$set(this.value, 'value', value);
this.value['value'] = value;
},
willSave() {
@ -172,7 +172,7 @@ export default {
useDefault() {
const configArr = this.parseValue(this.value.default);
this.$set(this, 'configArr', configArr);
this['configArr'] = configArr;
this.update();
}
}

View File

@ -15,11 +15,11 @@ export default {
methods: {
update() {
this.$set(this.value, 'value', String(this.terminationGracePeriodSeconds));
this.value['value'] = String(this.terminationGracePeriodSeconds);
},
useDefault() {
this.$set(this, 'terminationGracePeriodSeconds', Number(this.value.default));
this['terminationGracePeriodSeconds'] = Number(this.value.default);
this.update();
},
},

View File

@ -34,7 +34,7 @@ export default {
update() {
const value = JSON.stringify(this.parseDefaultValue);
this.$set(this.value, 'value', value);
this.value['value'] = value;
}
},
@ -43,7 +43,7 @@ export default {
handler(neu) {
const parseDefaultValue = JSON.parse(neu.value);
this.$set(this, 'parseDefaultValue', parseDefaultValue);
this['parseDefaultValue'] = parseDefaultValue;
},
deep: true
}

View File

@ -32,7 +32,7 @@ export default {
parseDefaultValue = { ntpServers: [] };
}
this.$set(this, 'parseDefaultValue', parseDefaultValue);
this['parseDefaultValue'] = parseDefaultValue;
this.update();
},
deep: true
@ -43,13 +43,13 @@ export default {
useDefault() {
const parseDefaultValue = { ntpServers: [] };
this.$set(this, 'parseDefaultValue', parseDefaultValue);
this['parseDefaultValue'] = parseDefaultValue;
},
update() {
const value = JSON.stringify(this.parseDefaultValue);
this.$set(this.value, 'value', value);
this.value['value'] = value;
},
},
};

View File

@ -32,7 +32,7 @@ export default {
update() {
const value = JSON.stringify(this.parseDefaultValue);
this.$set(this.value, 'value', value);
this.value['value'] = value;
}
},
@ -41,7 +41,7 @@ export default {
handler(neu) {
const parseDefaultValue = JSON.parse(neu.value);
this.$set(this, 'parseDefaultValue', parseDefaultValue);
this['parseDefaultValue'] = parseDefaultValue;
},
deep: true
}

View File

@ -30,11 +30,11 @@ export default {
onKeySelectedCa(type, file) {
const { name, value } = file;
this.$set(this.parseDefaultValue, type, value);
this.$set(this, `${ type }FileName`, name);
this.parseDefaultValue[type] = value;
this[`${ type }FileName`] = name;
const _value = JSON.stringify(this.parseDefaultValue);
this.$set(this.value, 'value', _value);
this.value['value'] = _value;
}
},
@ -43,7 +43,7 @@ export default {
handler(neu) {
const parseDefaultValue = JSON.parse(neu.value);
this.$set(this, 'parseDefaultValue', parseDefaultValue);
this['parseDefaultValue'] = parseDefaultValue;
},
deep: true
}

View File

@ -88,7 +88,7 @@ export default {
const value = JSON.stringify(out);
this.$set(this.value, 'value', value);
this.value['value'] = value;
},
willSave() {

View File

@ -114,9 +114,9 @@ export default {
const valueString = JSON.stringify(this.parsedDefaultValue);
if (this.openVlan) {
this.$set(this.value, 'value', valueString);
this.value['value'] = valueString;
} else {
this.$set(this.value, 'value', '');
this.value['value'] = '';
}
},

View File

@ -65,7 +65,7 @@ export default {
update() {
const value = JSON.stringify(this.parseDefaultValue);
this.$set(this.value, 'value', value);
this.value['value'] = value;
},
willSave() {
@ -92,7 +92,7 @@ export default {
handler(neu) {
const parseDefaultValue = JSON.parse(neu.value);
this.$set(this, 'parseDefaultValue', parseDefaultValue);
this['parseDefaultValue'] = parseDefaultValue;
},
deep: true
}

View File

@ -40,7 +40,7 @@ export default {
update() {
const namespaceStr = this.namespaces.join(',');
this.$set(this.value, 'value', namespaceStr);
this.value['value'] = namespaceStr;
}
},

View File

@ -30,7 +30,7 @@ export default {
update() {
const value = JSON.stringify(this.parseDefaultValue);
this.$set(this.value, 'value', value);
this.value['value'] = value;
}
},
@ -39,7 +39,7 @@ export default {
handler(neu) {
const parseDefaultValue = JSON.parse(neu.value);
this.$set(this, 'parseDefaultValue', parseDefaultValue);
this['parseDefaultValue'] = parseDefaultValue;
},
deep: true
}

View File

@ -104,7 +104,7 @@ export default {
key-field="_key"
v-on="$listeners"
>
<template slot="cell:state" slot-scope="scope" class="state-col">
<template slot="cell:state" slot-scope="scope">
<div class="state">
<HarvesterVmState class="vmstate" :row="scope.row" :all-cluster-network="allClusterNetwork" />
</div>

View File

@ -87,7 +87,7 @@ export default {
}
if (!this.value._type) {
this.$set(this.value, '_type', TYPES.OPAQUE);
this.value['_type'] = TYPES.OPAQUE;
}
return {

View File

@ -58,7 +58,7 @@ export default {
};
});
this.$set(this, 'sshKeys', out);
this['sshKeys'] = out;
}
}
};

View File

@ -146,7 +146,7 @@ export default {
handler(neu) {
const diskRows = this.getDiskRows(neu);
this.$set(this, 'diskRows', diskRows);
this['diskRows'] = diskRows;
},
deep: true
}

View File

@ -69,14 +69,14 @@ export default {
} else {
const error = [res?.data] || exceptionToErrorsArray(res);
this.$set(this, 'errors', error);
this['errors'] = error;
buttonCb(false);
}
} catch (err) {
const error = err?.data || err;
const message = exceptionToErrorsArray(error);
this.$set(this, 'errors', message);
this['errors'] = message;
buttonCb(false);
}
}

View File

@ -69,14 +69,14 @@ export default {
} else {
const error = [res?.data] || exceptionToErrorsArray(res);
this.$set(this, 'errors', error);
this['errors'] = error;
buttonCb(false);
}
} catch (err) {
const error = err?.data || err;
const message = exceptionToErrorsArray(error);
this.$set(this, 'errors', message);
this['errors'] = message;
buttonCb(false);
}
}

View File

@ -92,14 +92,14 @@ export default {
} else {
const error = [res?.data] || exceptionToErrorsArray(res);
this.$set(this, 'errors', error);
this['errors'] = error;
buttonCb(false);
}
} catch (err) {
const error = err?.data || err;
const message = exceptionToErrorsArray(error);
this.$set(this, 'errors', message);
this['errors'] = message;
buttonCb(false);
}
}

View File

@ -70,14 +70,14 @@ export default {
} else {
const error = [res?.data] || exceptionToErrorsArray(res);
this.$set(this, 'errors', error);
this['errors'] = error;
buttonCb(false);
}
} catch (err) {
const error = err?.data || err;
const message = exceptionToErrorsArray(error);
this.$set(this, 'errors', message);
this['errors'] = message;
buttonCb(false);
}
}

View File

@ -79,14 +79,14 @@ export default {
} else {
const error = [res?.data] || exceptionToErrorsArray(res);
this.$set(this, 'errors', error);
this['errors'] = error;
buttonCb(false);
}
} catch (err) {
const error = err?.data || err;
const message = exceptionToErrorsArray(error);
this.$set(this, 'errors', message);
this['errors'] = message;
buttonCb(false);
}
}

View File

@ -72,7 +72,7 @@ export default {
const error = err?.data || err;
const message = exceptionToErrorsArray(error);
this.$set(this, 'errors', message);
this['errors'] = message;
buttonDone(false);
}
},
@ -95,7 +95,7 @@ export default {
}
});
this.$set(this, 'diskNames', diskNames);
this['diskNames'] = diskNames;
},
deep: true
}

View File

@ -34,7 +34,7 @@ export default {
const defaultStorage = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS).find(s => s.isDefault);
this.$set(this, 'storageClassName', defaultStorage?.metadata?.name || 'longhorn');
this['storageClassName'] = defaultStorage?.metadata?.name || 'longhorn';
},
data() {
@ -119,14 +119,14 @@ export default {
} else {
const error = [res?.data] || exceptionToErrorsArray(res);
this.$set(this, 'errors', error);
this['errors'] = error;
buttonCb(false);
}
} catch (err) {
const error = err?.data || err;
const message = exceptionToErrorsArray(error);
this.$set(this, 'errors', message);
this['errors'] = message;
buttonCb(false);
}
}

View File

@ -109,7 +109,7 @@ export default {
const name = this.$store.getters['i18n/t']('harvester.modal.migration.fields.nodeName.label');
const message = this.$store.getters['i18n/t']('validation.required', { key: name });
this.$set(this, 'errors', [message]);
this['errors'] = [message];
buttonDone(false);
return;
@ -124,7 +124,7 @@ export default {
const error = err?.data || err;
const message = exceptionToErrorsArray(error);
this.$set(this, 'errors', message);
this['errors'] = message;
buttonDone(false);
}
},

View File

@ -105,14 +105,14 @@ export default {
} else {
const error = [res?.data] || exceptionToErrorsArray(res);
this.$set(this, 'errors', error);
this['errors'] = error;
buttonCb(false);
}
} catch (err) {
const error = err?.data || err;
const message = exceptionToErrorsArray(error);
this.$set(this, 'errors', message);
this['errors'] = message;
buttonCb(false);
}
}

View File

@ -62,14 +62,14 @@ export default {
} else {
const error = [res?.data] || exceptionToErrorsArray(res);
this.$set(this, 'errors', error);
this['errors'] = error;
buttonCb(false);
}
} catch (err) {
const error = err?.data || err;
const message = exceptionToErrorsArray(error);
this.$set(this, 'errors', message);
this['errors'] = message;
buttonCb(false);
}
}

View File

@ -86,7 +86,7 @@ export default {
const error = err?.data || err;
const message = exceptionToErrorsArray(error);
this.$set(this, 'errors', message);
this['errors'] = message;
buttonCb(false);
}
}

View File

@ -83,7 +83,7 @@ export default {
const error = err?.data || err;
const message = exceptionToErrorsArray(error);
this.$set(this, 'errors', message);
this['errors'] = message;
buttonDone(false);
}
},

View File

@ -46,7 +46,7 @@ export default {
const currentStorageName = this.resources[0].metadata?.annotations[HCI_ANNOTATIONS.STORAGE_CLASS];
this.$set(this, 'storageClassName', currentStorageName || defaultStorage?.metadata?.name || 'longhorn');
this['storageClassName'] = currentStorageName || defaultStorage?.metadata?.name || 'longhorn';
}
},
@ -108,14 +108,14 @@ export default {
} else {
const error = [res?.data] || exceptionToErrorsArray(res);
this.$set(this, 'errors', error);
this['errors'] = error;
buttonCb(false);
}
} catch (err) {
const error = err?.data || err;
const message = exceptionToErrorsArray(error);
this.$set(this, 'errors', message);
this['errors'] = message;
buttonCb(false);
}
}

View File

@ -50,14 +50,14 @@ export default {
} else {
const error = [res?.data] || exceptionToErrorsArray(res);
this.$set(this, 'errors', error);
this['errors'] = error;
buttonCb(false);
}
} catch (err) {
const error = err?.data || err;
const message = exceptionToErrorsArray(error);
this.$set(this, 'errors', message);
this['errors'] = message;
buttonCb(false);
}
}

View File

@ -61,14 +61,14 @@ export default {
} else {
const error = [res?.data] || exceptionToErrorsArray(res);
this.$set(this, 'errors', error);
this['errors'] = error;
buttonCb(false);
}
} catch (err) {
const error = err?.data || err;
const message = exceptionToErrorsArray(error);
this.$set(this, 'errors', message);
this['errors'] = message;
buttonCb(false);
}
}

View File

@ -77,7 +77,7 @@ export default {
watch: {
valuesContentJson: {
handler(neu) {
this.$set(this.value.spec, 'valuesContent', jsyaml.dump(neu));
this.value.spec['valuesContent'] = jsyaml.dump(neu);
},
deep: true,
immediate: true

View File

@ -191,7 +191,7 @@ export default {
watch: {
valuesContentJson: {
handler(neu) {
this.$set(this.value.spec, 'valuesContent', jsyaml.dump(neu));
this.value.spec['valuesContent'] = jsyaml.dump(neu);
},
deep: true,
immediate: true

View File

@ -80,7 +80,7 @@ export default {
watch: {
valuesContentJson: {
handler(neu) {
this.$set(this.value.spec, 'valuesContent', jsyaml.dump(neu));
this.value.spec['valuesContent'] = jsyaml.dump(neu);
},
deep: true,
immediate: true

View File

@ -108,7 +108,7 @@ export default {
const inStore = this.$store.getters['currentProduct'].inStore;
const defaultStorage = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS).find( s => s.isDefault);
this.$set(this.valuesContent.pvcClaim, 'storageClassName', this.valuesContent?.pvcClaim?.storageClassName || defaultStorage?.metadata?.name || 'longhorn');
this.valuesContent.pvcClaim['storageClassName'] = this.valuesContent?.pvcClaim?.storageClassName || defaultStorage?.metadata?.name || 'longhorn';
this.update();
},

View File

@ -5,7 +5,7 @@ import { BadgeState } from '@components/BadgeState';
import { Banner } from '@components/Banner';
import { RadioGroup, RadioButton } from '@components/Form/Radio';
import HarvesterDisk from '../../mixins/harvester-disk';
import Tags from '../../components/DiskTags';
import DiskTags from '../../components/DiskTags';
import { HCI } from '../../types';
import { LONGHORN_SYSTEM } from './index';
@ -17,7 +17,7 @@ export default {
Banner,
RadioGroup,
RadioButton,
Tags,
DiskTags,
},
mixins: [
@ -171,7 +171,7 @@ export default {
<div v-if="!value.isNew">
<div class="row">
<div class="col span-12">
<Tags
<DiskTags
v-model="value.tags"
:label="t('harvester.host.disk.tags.label')"
:add-label="t('harvester.host.disk.tags.addLabel')"

View File

@ -92,7 +92,7 @@ export default {
methods: {
async saveKsmtuned() {
this.spec.mergeAcrossNodes = this.enableMergeAcrossNodes ? 1 : 0;
this.$set(this.ksmtuned, 'spec', this.spec);
this.ksmtuned['spec'] = this.spec;
await this.ksmtuned.save().catch((reason) => {
if (reason?.type === 'error') {

View File

@ -123,8 +123,8 @@ export default {
} else {
const [namespace, name] = value.split('/');
this.$set(this.value.spec.baseboardSpec.connection.authSecretRef, 'namespace', namespace);
this.$set(this.value.spec.baseboardSpec.connection.authSecretRef, 'name', name);
this.value.spec.baseboardSpec.connection.authSecretRef['namespace'] = namespace;
this.value.spec.baseboardSpec.connection.authSecretRef['name'] = name;
}
},
},
@ -135,7 +135,7 @@ export default {
},
set(value) {
this.$set(this.secret.data, 'username', base64Encode(value));
this.secret.data['username'] = base64Encode(value);
}
},
@ -145,7 +145,7 @@ export default {
},
set(value) {
this.$set(this.secret.data, 'password', base64Encode(value));
this.secret.data['password'] = base64Encode(value);
}
},
},

View File

@ -25,7 +25,7 @@ import { _EDIT } from '@shell/config/query-params';
import { sortBy } from '@shell/utils/sort';
import { Banner } from '@components/Banner';
import { HCI } from '../../types';
import Tags from '../../components/DiskTags';
import DiskTags from '../../components/DiskTags';
import HarvesterDisk from './HarvesterDisk';
import HarvesterSeeder from './HarvesterSeeder';
import HarvesterKsmtuned from './HarvesterKsmtuned';
@ -46,7 +46,7 @@ export default {
ButtonDropdown,
KeyValue,
Banner,
Tags,
DiskTags,
Loading,
HarvesterSeeder,
MessageLink,
@ -546,7 +546,7 @@ export default {
class="row mb-20"
>
<div class="col span-12">
<Tags
<DiskTags
v-model="longhornNode.spec.tags"
:label="t('harvester.host.tags.label')"
:add-label="t('harvester.host.tags.addLabel')"

View File

@ -49,10 +49,10 @@ export default {
}));
if (this.mode !== _VIEW) {
this.$set(this.value, 'spec', this.value.spec || {});
this.value['spec'] = this.value.spec || {};
providers.forEach((provider) => {
this.$set(this.value.spec, provider.name, this.value.spec[provider.name] || clone(provider.default));
this.value.spec[provider.name] = this.value.spec[provider.name] || clone(provider.default);
});
}
@ -127,11 +127,11 @@ export default {
if (!isEmpty(bufferJson)) {
this.value.spec[this.selectedProvider].buffer = bufferJson;
} else {
this.$delete(this.value.spec[this.selectedProvider], 'buffer');
delete this.value.spec[this.selectedProvider]['buffer'];
}
if (this.loggingType === AUDIT_ONLY) {
this.$set(this.value.spec, 'loggingRef', 'harvester-kube-audit-log-ref');
this.value.spec['loggingRef'] = 'harvester-kube-audit-log-ref';
}
},
tabChanged({ tab }) {

View File

@ -52,9 +52,9 @@ export default {
if ( newCloudCred ) {
this.value.metadata.namespace = DEFAULT_WORKSPACE;
this.$set(this.value.metadata, 'name', '');
this.value.metadata['name'] = '';
this.$set(this.value, 'data', {});
this.value['data'] = {};
}
const secretTypes = [
@ -77,7 +77,7 @@ export default {
});
if ( this.mode === _CREATE ) {
this.$set(this.value, '_type', TYPES.OPAQUE);
this.value['_type'] = TYPES.OPAQUE;
}
return {
@ -212,7 +212,7 @@ export default {
selectCustomType(type) {
if (type !== 'custom') {
this.$set(this.value, '_type', type);
this.value['_type'] = type;
}
}
},

View File

@ -39,7 +39,7 @@ export default {
setting.canReset || !!this.value.default || this.value.canReset;
if (this.value.value === undefined) {
this.$set(this.value, 'value', null);
this.value['value'] = null;
}
this.value.value = this.value.value || this.value.default || '';

View File

@ -17,7 +17,7 @@ import { STORAGE_CLASS, LONGHORN } from '@shell/config/types';
import { allHash } from '@shell/utils/promise';
import { clone } from '@shell/utils/object';
import { CSI_DRIVER } from '../../types';
import Tags from '../../components/DiskTags';
import DiskTags from '../../components/DiskTags';
const LONGHORN_DRIVER = 'driver.longhorn.io';
@ -34,7 +34,7 @@ export default {
Tab,
Tabbed,
Loading,
Tags,
DiskTags,
},
mixins: [CreateEditView],
@ -72,11 +72,11 @@ export default {
const allowedTopologies = clone(this.value.allowedTopologies?.[0]?.matchLabelExpressions || []);
this.$set(this.value, 'parameters', this.value.parameters || {});
this.$set(this.value, 'provisioner', this.value.provisioner || LONGHORN_DRIVER);
this.$set(this.value, 'allowVolumeExpansion', this.value.allowVolumeExpansion || allowVolumeExpansionOptions[0].value);
this.$set(this.value, 'reclaimPolicy', this.value.reclaimPolicy || reclaimPolicyOptions[0].value);
this.$set(this.value, 'volumeBindingMode', this.value.volumeBindingMode || volumeBindingModeOptions[0].value);
this.value['parameters'] = this.value.parameters || {};
this.value['provisioner'] = this.value.provisioner || LONGHORN_DRIVER;
this.value['allowVolumeExpansion'] = this.value.allowVolumeExpansion || allowVolumeExpansionOptions[0].value;
this.value['reclaimPolicy'] = this.value.reclaimPolicy || reclaimPolicyOptions[0].value;
this.value['volumeBindingMode'] = this.value.volumeBindingMode || volumeBindingModeOptions[0].value;
return {
reclaimPolicyOptions,
@ -141,7 +141,7 @@ export default {
watch: {
provisionerWatch() {
this.$set(this.value, 'parameters', {});
this.value['parameters'] = {};
}
},
@ -159,8 +159,8 @@ export default {
},
updateProvisioner(provisioner) {
this.$set(this.value, 'provisioner', provisioner);
this.$set(this.value, 'allowVolumeExpansion', provisioner === LONGHORN_DRIVER);
this.value['provisioner'] = provisioner;
this.value['allowVolumeExpansion'] = provisioner === LONGHORN_DRIVER;
},
willSave() {
@ -303,7 +303,7 @@ export default {
/>
</div>
<div class="col span-8 value">
<Tags
<DiskTags
v-model="scope.row.value.values"
:add-label="t('generic.add')"
:mode="modeOverride"

View File

@ -42,13 +42,13 @@ export default {
data() {
if (this.realMode === _CREATE) {
this.$set(this.value, 'parameters', {
this.value['parameters'] = {
numberOfReplicas: '3',
staleReplicaTimeout: '30',
diskSelector: null,
nodeSelector: null,
migratable: 'true',
});
migratable: 'true'
};
}
return {};

View File

@ -56,12 +56,12 @@ export default {
const defaultStorage = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS).find(s => s.isDefault);
this.$set(this, 'storageClassName', this.storageClassName || defaultStorage?.metadata?.name || 'longhorn');
this['storageClassName'] = this.storageClassName || defaultStorage?.metadata?.name || 'longhorn';
},
data() {
if ( !this.value.spec ) {
this.$set(this.value, 'spec', { sourceType: DOWNLOAD });
this.value['spec'] = { sourceType: DOWNLOAD };
}
if (!this.value.metadata.name) {
@ -130,7 +130,7 @@ export default {
},
'value.spec.sourceType'() {
this.$set(this, 'file', null);
this['file'] = null;
this.url = '';
if (this.$refs?.file?.value) {

View File

@ -175,10 +175,10 @@ export default {
this.value.cleanForNew();
this.customName = randomStr(10);
this.$set(this.value.metadata, 'annotations', {
this.value.metadata['annotations'] = {
...this.value.metadata.annotations,
[HCI_ANNOTATIONS.TEMPLATE_VERSION_CUSTOM_NAME]: this.customName
});
};
const name = this.templateValue.metadata.name || template.metadata.name;
const namespace = this.templateValue.metadata.namespace || template.metadata.namespace;
@ -187,7 +187,7 @@ export default {
this.value.metadata.namespace = namespace;
}
this.$set(this.value.spec, 'templateId', `${ namespace }/${ name }`);
this.value.spec['templateId'] = `${ namespace }/${ name }`;
const res = await this.value.save();
await this.saveSecret(res);

View File

@ -59,7 +59,7 @@ export default {
const defaultStorage = this.$store.getters[`harvester/all`](STORAGE_CLASS).find( O => O.isDefault);
this.$set(this.value.spec, 'storageClassName', this.value?.spec?.storageClassName || defaultStorage?.metadata?.name || 'longhorn');
this.value.spec['storageClassName'] = this.value?.spec?.storageClassName || defaultStorage?.metadata?.name || 'longhorn';
},
data() {
@ -247,7 +247,7 @@ export default {
this.value.setAnnotations(imageAnnotations);
this.$set(this.value, 'spec', spec);
this.value['spec'] = spec;
},
generateYaml() {

View File

@ -73,7 +73,7 @@ export default {
selectedUser(val, old) {
if ( val.includes(_NEW)) {
this.$set(this, 'selectedUser', old);
this['selectedUser'] = old;
this.update();
this.show();
}

View File

@ -49,7 +49,7 @@ export default {
},
updateUser(neu) {
this.$set(this.value, 'username', neu);
this.value['username'] = neu;
this.update();
},

View File

@ -40,12 +40,12 @@ export default {
},
updateUser(neu) {
this.$set(this.value, 'users', neu);
this.value['users'] = neu;
this.update();
},
updateSSH(neu) {
this.$set(this.value, 'sshkeys', neu);
this.value['sshkeys'] = neu;
this.update();
},

View File

@ -179,7 +179,7 @@ export default {
cancel() {
this.cloudTemplate = '';
this.cloudTemplateName = '';
this.$set(this, 'errors', []);
this['errors'] = [];
this.isOpen = false;
},

View File

@ -138,7 +138,7 @@ export default {
handler(neu) {
const hasManagementNetwork = !!neu.some(N => N.isPod);
this.$set(this, 'hasManagementNetwork', hasManagementNetwork);
this['hasManagementNetwork'] = hasManagementNetwork;
},
immediate: true,
deep: true
@ -146,7 +146,7 @@ export default {
isSingle(neu) {
if (!neu) {
this.$set(this.value, 'macAddress', '');
this.value['macAddress'] = '';
this.update();
}
}
@ -164,7 +164,7 @@ export default {
this.value.isPod = false;
}
this.$set(this, 'isMasquerade', this.value.isPod);
this['isMasquerade'] = this.value.isPod;
if (this.value.isPod) {
this.value.type = 'masquerade';

View File

@ -134,8 +134,8 @@ export default {
},
changeRows(filterRows, parentSriov) {
this.$set(this, 'filterRows', filterRows);
this.$set(this, 'parentSriov', parentSriov);
this['filterRows'] = filterRows;
this['parentSriov'] = parentSriov;
},
sortGenerationFn() {

View File

@ -126,7 +126,7 @@ export default {
checkedSsh(val, old) {
if ( val.includes(_NEW)) {
this.$set(this, 'checkedSsh', old);
this['checkedSsh'] = old;
this.update();
this.show();
}

View File

@ -89,8 +89,8 @@ export default {
methods: {
changeRows(filterRows, parentSriov) {
this.$set(this, 'filterRows', filterRows);
this.$set(this, 'parentSriov', parentSriov);
this['filterRows'] = filterRows;
this['parentSriov'] = parentSriov;
},
sortGenerationFn() {

View File

@ -149,7 +149,7 @@ export default {
return V;
});
this.$set(this, 'rows', rows);
this['rows'] = rows;
},
deep: true,
immediate: true,

View File

@ -32,7 +32,7 @@ export default {
watch: {
'value.type'(neu) {
if (neu === 'cd-rom') {
this.$set(this.value, 'bus', 'sata');
this.value['bus'] = 'sata';
this.update();
}
},

View File

@ -140,7 +140,7 @@ export default {
'value.type'(neu) {
if (neu === 'cd-rom') {
this.$set(this.value, 'bus', 'sata');
this.value['bus'] = 'sata';
this.update();
}
},

View File

@ -100,7 +100,7 @@ export default {
watch: {
'value.type'(neu) {
if (neu === 'cd-rom') {
this.$set(this.value, 'bus', 'sata');
this.value['bus'] = 'sata';
this.update();
}
},
@ -143,11 +143,11 @@ export default {
if (this.idx === 0) {
if (/iso$/i.test(imageResource?.imageSuffix)) {
this.$set(this.value, 'type', 'cd-rom');
this.$set(this.value, 'bus', 'sata');
this.value['type'] = 'cd-rom';
this.value['bus'] = 'sata';
} else {
this.$set(this.value, 'type', 'disk');
this.$set(this.value, 'bus', 'virtio');
this.value['type'] = 'disk';
this.value['bus'] = 'virtio';
}
}
@ -165,7 +165,7 @@ export default {
message: this.$store.getters['i18n/t']('harvester.vmTemplate.tips.notExistImage.message')
}, { root: true });
this.$set(this.value, 'image', '');
this.value['image'] = '';
}
}
}

View File

@ -87,7 +87,7 @@ export default {
watch: {
'value.type'(neu) {
if (neu === 'cd-rom') {
this.$set(this.value, 'bus', 'sata');
this.value['bus'] = 'sata';
this.update();
}
},

View File

@ -19,17 +19,17 @@ import KeyValue from '@shell/components/form/KeyValue';
import { clear } from '@shell/utils/array';
import { clone } from '@shell/utils/object';
import { HCI } from '../../types';
import { RunStrategys } from '../../config/harvester-map';
import { saferDump } from '@shell/utils/create-yaml';
import { exceptionToErrorsArray } from '@shell/utils/error';
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
import { BEFORE_SAVE_HOOKS, AFTER_SAVE_HOOKS } from '@shell/mixins/child-hook';
import VM_MIXIN from '../../mixins/harvester-vm';
import CreateEditView from '@shell/mixins/create-edit-view';
import { parseVolumeClaimTemplates } from '@pkg/utils/vm';
import VM_MIXIN from '../../mixins/harvester-vm';
import { RunStrategys } from '../../config/harvester-map';
import { HCI } from '../../types';
import RestartVMDialog from '../../dialog/RestartVMDialog';
import VirtualMachineVGpuDevices from './VirtualMachineVGpuDevices/index';
import PciDevices from './VirtualMachinePciDevices/index';
@ -218,7 +218,7 @@ export default {
this.getInitConfig({
value: cloneVersionVM, existUserData: true, fromTemplate: true
});
this.$set(this, 'hasCreateVolumes', []); // When using the template, all volume names need to be newly created
this['hasCreateVolumes'] = []; // When using the template, all volume names need to be newly created
}
},
@ -261,7 +261,7 @@ export default {
const diskRows = this.getDiskRows(this.value);
this.$set(this, 'diskRows', diskRows);
this['diskRows'] = diskRows;
const templateId = this.$route.query.templateId;
const templateVersionId = this.$route.query.versionId;
@ -317,13 +317,13 @@ export default {
const cloneSpec = clone(this.spec);
for (let i = 1; i <= this.count; i++) {
this.$set(this.value, 'spec', cloneValue.spec);
this.$set(this, 'spec', cloneSpec);
this.value['spec'] = cloneValue.spec;
this['spec'] = cloneSpec;
const suffix = i < 10 ? `0${ i }` : i;
this.value.cleanForNew();
this.value.metadata.name = `${ this.namePrefix }${ join }${ suffix }`;
this.$set(this.value.spec.template.spec, 'hostname', `${ baseHostname }${ join }${ suffix }`);
this.value.spec.template.spec['hostname'] = `${ baseHostname }${ join }${ suffix }`;
this.secretName = '';
await this.parseVM();
const basicValue = await this.$store.dispatch('harvester/clone', { resource: this.value });
@ -380,7 +380,7 @@ export default {
updateBeforeSave() {
if (this.isSingle) {
if (!this.value.spec.template.spec.hostname) {
this.$set(this.value.spec.template.spec, 'hostname', this.value.metadata.name);
this.value.spec.template.spec['hostname'] = this.value.metadata.name;
}
}
@ -395,7 +395,7 @@ export default {
validateCount(count) {
if (count > 10) {
this.$set(this, 'count', 10);
this['count'] = 10;
}
},

View File

@ -139,7 +139,7 @@ export default {
backendServerSelector[key] = (value[key] || '').split(',');
});
this.$set(this.value.spec, 'backendServerSelector', backendServerSelector);
this.value.spec['backendServerSelector'] = backendServerSelector;
},
},

View File

@ -36,8 +36,8 @@ export default {
this.canEditPSPBindings = !!pspBindingSchema;
},
data() {
this.$set(this.value, 'spec', this.value.spec || {});
this.$set(this.value.spec, 'podSecurityPolicyTemplateId', this.value.status?.podSecurityPolicyTemplateId || '');
this.value['spec'] = this.value.spec || {};
this.value.spec['podSecurityPolicyTemplateId'] = this.value.status?.podSecurityPolicyTemplateId || '';
return {
allPSPs: [],
@ -134,9 +134,9 @@ export default {
}
},
created() {
this.$set(this.value.metadata, 'namespace', this.$store.getters['currentCluster'].id);
this.$set(this.value, 'spec', this.value.spec || {});
this.$set(this.value.spec, 'containerDefaultResourceLimit', this.value.spec.containerDefaultResourceLimit || {});
this.value.metadata['namespace'] = this.$store.getters['currentCluster'].id;
this.value['spec'] = this.value.spec || {};
this.value.spec['containerDefaultResourceLimit'] = this.value.spec.containerDefaultResourceLimit || {};
},
methods: {
async save(saveCb) {
@ -178,11 +178,11 @@ export default {
},
onHasOwnerChanged(hasOwner) {
this.$set(this, 'membershipHasOwner', hasOwner);
this['membershipHasOwner'] = hasOwner;
},
onMembershipUpdate(update) {
this.$set(this, 'membershipUpdate', update);
this['membershipUpdate'] = update;
},
removeQuota(key) {

View File

@ -39,8 +39,8 @@ export default {
methods: {
changeRows(filterRows, searchLabels) {
this.$set(this, 'filterRows', filterRows);
this.$set(this, 'searchLabels', searchLabels);
this['filterRows'] = filterRows;
this['searchLabels'] = searchLabels;
},
sortGenerationFn() {

View File

@ -161,7 +161,7 @@ export default {
await this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.VMIM });
this.$set(this, 'allVMIs', vmis);
this['allVMIs'] = vmis;
}
};
</script>
@ -179,7 +179,7 @@ export default {
key-field="_key"
v-on="$listeners"
>
<template slot="cell:state" slot-scope="scope" class="state-col">
<template slot="cell:state" slot-scope="scope">
<div class="state">
<HarvesterVmState class="vmstate" :row="scope.row" :all-node-network="allNodeNetworks" :all-cluster-network="allClusterNetworks" />
</div>

View File

@ -361,33 +361,33 @@ export default {
spec.runStrategy = 'RerunOnFailure';
}
this.$set(this, 'spec', spec);
this.$set(this, 'runStrategy', runStrategy);
this.$set(this, 'secretRef', secretRef);
this.$set(this, 'accessCredentials', accessCredentials);
this.$set(this, 'userScript', userData);
this.$set(this, 'networkScript', networkData);
this['spec'] = spec;
this['runStrategy'] = runStrategy;
this['secretRef'] = secretRef;
this['accessCredentials'] = accessCredentials;
this['userScript'] = userData;
this['networkScript'] = networkData;
this.$set(this, 'sshKey', sshKey);
this.$set(this, 'osType', osType);
this.$set(this, 'installAgent', installAgent);
this['sshKey'] = sshKey;
this['osType'] = osType;
this['installAgent'] = installAgent;
this.$set(this, 'cpu', cpu);
this.$set(this, 'memory', memory);
this.$set(this, 'reservedMemory', reservedMemory);
this.$set(this, 'machineType', machineType);
this.$set(this, 'terminationGracePeriodSeconds', terminationGracePeriodSeconds);
this['cpu'] = cpu;
this['memory'] = memory;
this['reservedMemory'] = reservedMemory;
this['machineType'] = machineType;
this['terminationGracePeriodSeconds'] = terminationGracePeriodSeconds;
this.$set(this, 'installUSBTablet', installUSBTablet);
this.$set(this, 'efiEnabled', efiEnabled);
this.$set(this, 'tpmEnabled', tpmEnabled);
this.$set(this, 'secureBoot', secureBoot);
this['installUSBTablet'] = installUSBTablet;
this['efiEnabled'] = efiEnabled;
this['tpmEnabled'] = tpmEnabled;
this['secureBoot'] = secureBoot;
this.$set(this, 'hasCreateVolumes', hasCreateVolumes);
this.$set(this, 'networkRows', networkRows);
this.$set(this, 'imageId', imageId);
this['hasCreateVolumes'] = hasCreateVolumes;
this['networkRows'] = networkRows;
this['imageId'] = imageId;
this.$set(this, 'diskRows', diskRows);
this['diskRows'] = diskRows;
this.refreshYamlEditor();
},
@ -556,9 +556,9 @@ export default {
parseOther() {
if (!this.spec.template.spec.domain.machine) {
this.$set(this.spec.template.spec.domain, 'machine', { type: this.machineType });
this.spec.template.spec.domain['machine'] = { type: this.machineType };
} else {
this.$set(this.spec.template.spec.domain.machine, 'type', this.machineType);
this.spec.template.spec.domain.machine['type'] = this.machineType;
}
this.spec.template.spec.domain.cpu.cores = this.cpu;
@ -681,28 +681,28 @@ export default {
spec = this.multiVMScheduler(spec);
}
this.$set(this.value.metadata, 'annotations', {
this.value.metadata['annotations'] = {
...this.value.metadata.annotations,
[HCI_ANNOTATIONS.VOLUME_CLAIM_TEMPLATE]: JSON.stringify(volumeClaimTemplates),
[HCI_ANNOTATIONS.NETWORK_IPS]: JSON.stringify(this.value.networkIps)
});
};
this.$set(this.value.metadata, 'labels', {
this.value.metadata['labels'] = {
...this.value.metadata.labels,
[HCI_ANNOTATIONS.CREATOR]: 'harvester',
[HCI_ANNOTATIONS.OS]: this.osType
});
};
this.$set(this.value, 'spec', spec);
this.$set(this, 'spec', spec);
this.value['spec'] = spec;
this['spec'] = spec;
} else if (this.resource === HCI.VM_VERSION) {
this.$set(this.value.spec.vm, 'spec', spec);
this.$set(this.value.spec.vm.metadata, 'annotations', { ...this.value.spec.vm.metadata.annotations, [HCI_ANNOTATIONS.VOLUME_CLAIM_TEMPLATE]: JSON.stringify(volumeClaimTemplates) });
this.$set(this.value.spec.vm.metadata, 'labels', {
this.value.spec.vm['spec'] = spec;
this.value.spec.vm.metadata['annotations'] = { ...this.value.spec.vm.metadata.annotations, [HCI_ANNOTATIONS.VOLUME_CLAIM_TEMPLATE]: JSON.stringify(volumeClaimTemplates) };
this.value.spec.vm.metadata['labels'] = {
...this.value.spec.vm.metadata.labels,
[HCI_ANNOTATIONS.OS]: this.osType,
});
this.$set(this, 'spec', spec);
[HCI_ANNOTATIONS.OS]: this.osType
};
this['spec'] = spec;
}
},
@ -772,7 +772,7 @@ export default {
networks
};
this.$set(this.spec.template, 'spec', spec);
this.spec.template['spec'] = spec;
},
parseAccessCredentials() {
@ -862,12 +862,12 @@ export default {
},
updateSSHKey(neu) {
this.$set(this, 'sshKey', neu);
this['sshKey'] = neu;
},
updateCpuMemory(cpu, memory) {
this.$set(this, 'cpu', cpu);
this.$set(this, 'memory', memory);
this['cpu'] = cpu;
this['memory'] = memory;
},
parseDisk(R, index) {
@ -1309,10 +1309,10 @@ export default {
const index = inputs.findIndex(O => isEqual(O, USB_TABLET[0]));
if (hasExist && inputs.length === 1) {
this.$delete(this.spec.template.spec.domain.devices, 'inputs');
delete this.spec.template.spec.domain.devices['inputs'];
} else if (hasExist) {
inputs.splice(index, 1);
this.$set(this.spec.template.spec.domain.devices, 'inputs', inputs);
this.spec.template.spec.domain.devices['inputs'] = inputs;
}
}
},
@ -1325,17 +1325,17 @@ export default {
// set(this.spec.template.spec.domain, 'features.smm.enabled', false);
try {
this.$delete(this.spec.template.spec.domain.features.smm, 'enabled');
delete this.spec.template.spec.domain.features.smm['enabled'];
const noKeys = Object.keys(this.spec.template.spec.domain.features.smm).length === 0;
if (noKeys) {
this.$delete(this.spec.template.spec.domain.features, 'smm');
delete this.spec.template.spec.domain.features['smm'];
}
} catch (e) {}
set(this.spec.template.spec.domain, 'firmware.bootloader.efi.secureBoot', false);
} else {
this.$delete(this.spec.template.spec.domain, 'firmware');
this.$delete(this.spec.template.spec.domain.features, 'smm');
delete this.spec.template.spec.domain['firmware'];
delete this.spec.template.spec.domain.features['smm'];
}
},
@ -1343,7 +1343,7 @@ export default {
if (tpmEnabled) {
set(this.spec.template.spec.domain.devices, 'tpm', {});
} else {
this.$delete(this.spec.template.spec.domain.devices, 'tpm');
delete this.spec.template.spec.domain.devices['tpm'];
}
},
@ -1367,9 +1367,9 @@ export default {
}
if (isEmpty(userDataJson)) {
this.$set(this, 'userScript', undefined);
this['userScript'] = undefined;
} else {
this.$set(this, 'userScript', jsyaml.dump(userDataJson));
this['userScript'] = jsyaml.dump(userDataJson);
}
this.refreshYamlEditor();
@ -1407,11 +1407,11 @@ export default {
updateReserved(value = {}) {
const { memory } = value;
this.$set(this, 'reservedMemory', memory);
this['reservedMemory'] = memory;
},
updateTerminationGracePeriodSeconds(value) {
this.$set(this, 'terminationGracePeriodSeconds', value);
this['terminationGracePeriodSeconds'] = value;
},
},
@ -1444,9 +1444,9 @@ export default {
isWindows(val) {
if (val) {
this.$set(this, 'sshKey', []);
this.$set(this, 'userScript', undefined);
this.$set(this, 'installAgent', false);
this['sshKey'] = [];
this['userScript'] = undefined;
this['installAgent'] = false;
}
},
@ -1487,7 +1487,7 @@ export default {
}
}
this.$set(this, 'userScript', out);
this['userScript'] = out;
this.refreshYamlEditor();
}
this.deleteAgent = true;
@ -1498,7 +1498,7 @@ export default {
osType(neu) {
const out = this.getUserData({ installAgent: this.installAgent, osType: neu });
this.$set(this, 'userScript', out);
this['userScript'] = out;
this.refreshYamlEditor();
},

View File

@ -1,4 +1,3 @@
import Vue from 'vue';
import { _CLONE } from '@shell/config/query-params';
import pick from 'lodash/pick';
import { PV, LONGHORN } from '@shell/config/types';
@ -21,12 +20,12 @@ export default class HciPv extends HarvesterResource {
const storageClassName =
realMode === _CLONE ? this.spec.storageClassName : '';
Vue.set(this, 'spec', {
this['spec'] = {
accessModes,
storageClassName,
volumeName: '',
resources: { requests: { storage } }
});
};
}
get availableActions() {

View File

@ -1,4 +1,3 @@
import Vue from 'vue';
import {
DESCRIPTION,
ANNOTATIONS_TO_IGNORE_REGEX,
@ -64,8 +63,8 @@ export default class HciVmImage extends HarvesterResource {
applyDefaults(resources = this, realMode) {
if (realMode !== _CLONE) {
Vue.set(this.metadata, 'labels', { [HCI_ANNOTATIONS.OS_TYPE]: '', [HCI_ANNOTATIONS.IMAGE_SUFFIX]: '' });
Vue.set(this.metadata, 'annotations', { [HCI_ANNOTATIONS.STORAGE_CLASS]: '' });
this.metadata['labels'] = { [HCI_ANNOTATIONS.OS_TYPE]: '', [HCI_ANNOTATIONS.IMAGE_SUFFIX]: '' };
this.metadata['annotations'] = { [HCI_ANNOTATIONS.STORAGE_CLASS]: '' };
}
}

View File

@ -1,4 +1,3 @@
import Vue from 'vue';
import { find, pickBy, omitBy } from 'lodash';
import {
AS, MODE, _VIEW, _CONFIG, _UNFLAG, _EDIT
@ -110,7 +109,7 @@ export default class HciVmTemplateVersion extends HarvesterResource {
}
};
Vue.set(this, 'spec', spec);
this['spec'] = spec;
}
get canDelete() {
@ -272,6 +271,6 @@ export default class HciVmTemplateVersion extends HarvesterResource {
return matchesSomeRegex(key, LABELS_TO_IGNORE_REGEX);
});
Vue.set(this.spec.vm.spec.template.metadata, 'labels', { ...wasIgnored, ...val });
this.spec.vm.spec.template.metadata['labels'] = { ...wasIgnored, ...val };
}
}

View File

@ -1,4 +1,3 @@
import Vue from 'vue';
import SteveModel from '@shell/plugins/steve/steve-class';
import { HCI } from '@shell/config/labels-annotations';
@ -16,7 +15,7 @@ export default class NetworkAttachmentDef extends SteveModel {
})
};
Vue.set(this, 'spec', spec);
this.spec = spec;
}
get parseConfig() {

View File

@ -1,4 +1,3 @@
import Vue from 'vue';
import { load } from 'js-yaml';
import { omitBy, pickBy } from 'lodash';
@ -264,8 +263,8 @@ export default class VirtVm extends HarvesterResource {
};
if (realMode !== _CLONE) {
Vue.set(this.metadata, 'annotations', { [HCI_ANNOTATIONS.VOLUME_CLAIM_TEMPLATE]: '[]' });
Vue.set(this, 'spec', spec);
this.metadata['annotations'] = { [HCI_ANNOTATIONS.VOLUME_CLAIM_TEMPLATE]: '[]' };
this['spec'] = spec;
}
}
@ -1057,6 +1056,6 @@ export default class VirtVm extends HarvesterResource {
return matchesSomeRegex(key, LABELS_TO_IGNORE_REGEX);
});
Vue.set(this.spec.template.metadata, 'labels', { ...wasIgnored, ...val });
this.spec.template.metadata['labels'] = { ...wasIgnored, ...val };
}
}

View File

@ -1,4 +1,3 @@
import Vue from 'vue';
import { clone } from '@shell/utils/object';
import HarvesterResource from '@pkg/harvester/models/harvester';
import { HCI } from '@pkg/harvester/types';
@ -14,8 +13,8 @@ export default class HciLB extends HarvesterResource {
spec.selector = spec.selector || {};
spec.selector.network = spec.selector.network || '';
Vue.set(this, 'spec', spec);
Vue.set(this, 'metadata', meta);
this['spec'] = spec;
this['metadata'] = meta;
}
get customValidationRules() {

View File

@ -1,5 +1,6 @@
<script lang="ts">
import Vue from 'vue';
// eslint-disable-next-line import/named
import { Location } from 'vue-router';
import ExplorerProjectsNamespaces from '@shell/components/ExplorerProjectsNamespaces.vue';
import { MANAGEMENT, NAMESPACE } from '@shell/config/types';

View File

@ -1,4 +1,3 @@
import Vue from 'vue';
import Parse from 'url-parse';
import { HCI } from '../types';
import { PRODUCT_NAME } from '../config/harvester';
@ -42,7 +41,7 @@ const mutations = {
uploadEnd(state, value) {
const filtered = state.uploadingImages.filter(l => l !== value);
Vue.set(state, 'uploadingImages', filtered);
state['uploadingImages'] = filtered;
}
};

683
scripts/vue-migrate.js Normal file
View File

@ -0,0 +1,683 @@
#!/usr/bin/node
/* eslint-disable no-console */
const fs = require('fs');
const path = require('path');
const glob = require('glob');
const semver = require('semver');
/**
* Init logger
*/
const stats = {
libraries: [],
node: [],
githubActions: [],
nvmrc: [],
webpack: [],
jest: [],
router: [],
resolution: [],
eslint: [],
vueSyntax: [],
style: [],
total: [],
};
const ignore = [
'**/node_modules/**',
'**/dist/**',
'**/scripts/vue-migrate.js',
'docusaurus/**',
'storybook-static/**',
'storybook/**',
];
const nodeRequirement = '20.0.0';
const isDry = process.argv.includes('--dry');
const isVerbose = process.argv.includes('--verbose');
const removePlaceholder = 'REMOVE';
const params = { paths: null };
/**
* Package updates
* Files: package.json
*/
const packageUpdates = () => {
const files = glob.sync(params.paths || '**/package.json', { ignore });
files.forEach((file) => {
let content = fs.readFileSync(file, 'utf8');
const toReplaceNode = false;
// TODO: Refactor and loop?
const [librariesContent, replaceLibraries] = packageUpdatesLibraries(file, content);
if (replaceLibraries.length) {
content = librariesContent;
printContent(file, `Updating`, replaceLibraries);
stats.libraries.push(file);
}
const [nodeContent, replaceNode] = packageUpdatesEngine(file, content);
if (replaceNode.length) {
printContent(file, `Updating node`, replaceNode);
content = nodeContent;
stats.node.push(file);
}
const [resolutionContent, replaceResolution] = packageUpdatesResolution(file, content);
if (replaceResolution.length) {
printContent(file, `Updating resolution`, replaceResolution);
content = resolutionContent;
stats.libraries.push(file);
}
if (replaceLibraries || toReplaceNode || replaceResolution) {
stats.total.push(file);
}
});
};
/**
* Verify package vue related libraries versions
*/
const packageUpdatesLibraries = (file, oldContent) => {
let content = oldContent;
let parsedJson = JSON.parse(content);
const replaceLibraries = [];
const types = ['dependencies', 'devDependencies', 'peerDependencies'];
// [Library name, new version or new library, new library version]
const librariesUpdates = [
['@nuxt/babel-preset-app', removePlaceholder],
['@types/jest', '^29.5.2'],
['@typescript-eslint/eslint-plugin', '~5.4.0'],
['@typescript-eslint/parser', '~5.4.0'],
['@vue/cli-plugin-babel', '~5.0.0'],
['@vue/cli-plugin-e2e-cypress', '~5.0.0'],
['@vue/cli-plugin-eslint', '~5.0.0'],
['@vue/cli-plugin-router', '~5.0.0'],
['@vue/cli-plugin-typescript', '~5.0.0'],
['@vue/cli-plugin-unit-jest', '~5.0.0'],
['@vue/cli-plugin-vuex', '~5.0.0'],
['@vue/cli-service', '~5.0.0'],
['@vue/eslint-config-typescript', '~9.1.0'],
['@vue/vue2-jest', '@vue/vue3-jest', '^27.0.0-alpha.1'],
['@vue/test-utils', '~2.0.0-0'],
['core-js', '3.25.3'],
['cache-loader', '^4.1.0'],
['node-polyfill-webpack-plugin', '^3.0.0'],
['portal-vue', '~3.0.0'],
['require-extension-hooks-babel', '1.0.0'],
['require-extension-hooks-vue', '3.0.0'],
['require-extension-hooks', '0.3.3'],
['sass-loader', '~12.0.0'],
['typescript', '~4.5.5'],
['vue-router', '~4.0.3'],
['vue-virtual-scroll-list', 'vue3-virtual-scroll-list', '0.2.1'],
['vue', '~3.2.13'],
['vuex', '~4.0.0'],
['xterm', '5.2.1'],
];
// Loop through each type of dependencies since many often not correctly placed or hard to track
types.forEach((type) => {
if (parsedJson[type]) {
librariesUpdates.forEach(([library, newVersion, newLibraryVersion]) => {
if (parsedJson[type][library]) {
const version = semver.coerce(parsedJson[type][library]);
if (newVersion === removePlaceholder) {
// Remove library
replaceLibraries.push([library, [parsedJson[type][library], removePlaceholder]]);
delete parsedJson[type][library];
content = JSON.stringify(parsedJson, null, 2);
writeContent(file, content);
} else if (newLibraryVersion) {
// Replace with a new library if present, due breaking changes in Vue3
replaceLibraries.push([library, [parsedJson[type][library], newVersion, newLibraryVersion]]);
content = content.replaceAll(`"${ library }": "${ parsedJson[type][library] }"`, `"${ newVersion }": "${ newLibraryVersion }"`);
parsedJson = JSON.parse(content);
writeContent(file, content);
} else if (version && semver.lt(version, semver.coerce(newVersion))) {
// Update library version if outdated
replaceLibraries.push([library, [parsedJson[type][library], newVersion]]);
content = content.replaceAll(`"${ library }": "${ parsedJson[type][library] }"`, `"${ library }": "${ newVersion }"`);
parsedJson = JSON.parse(content);
writeContent(file, content);
}
}
});
}
});
return [content, replaceLibraries];
};
/**
* Verify package engines node to latest
*/
const packageUpdatesEngine = (file, oldContent) => {
let content = oldContent;
let parsedJson = JSON.parse(content);
const replaceNode = [];
// Verify package engines node to latest
if (parsedJson.engines) {
const outdated = semver.lt(semver.coerce(parsedJson.engines.node), semver.coerce(nodeRequirement));
if (outdated) {
replaceNode.push([parsedJson.engines.node, nodeRequirement]);
content = content.replaceAll(`"node": "${ parsedJson.engines.node }"`, `"node": ">=${ nodeRequirement }"`);
parsedJson = JSON.parse(content);
writeContent(file, content);
}
}
return [content, replaceNode];
};
/**
* Add resolutions for VueCLI
*/
const packageUpdatesResolution = (file, oldContent) => {
let content = oldContent;
let parsedJson = JSON.parse(content);
const replaceResolution = [];
const resolutions = [
['@vue/cli-service/html-webpack-plugin', '^5.0.0'],
['**/webpack', removePlaceholder],
];
// Verify package engines node to latest
if (parsedJson.resolutions) {
resolutions.forEach(([library, newVersion]) => {
if (newVersion === removePlaceholder) {
delete parsedJson.resolutions[library];
content = JSON.stringify(parsedJson, null, 2);
parsedJson = JSON.parse(content);
writeContent(file, content);
} else if (!parsedJson.resolutions[library]) {
// Add resolution if not present
parsedJson.resolutions[library] = newVersion;
content = JSON.stringify(parsedJson, null, 2);
parsedJson = JSON.parse(content);
writeContent(file, content);
} else {
// Ensure resolution version is up to date
const outdated = semver.lt(semver.coerce(parsedJson.resolutions[library]), semver.coerce(newVersion));
if (outdated) {
replaceResolution.push([parsedJson.engines.node, nodeRequirement]);
content = content.replaceAll(`"${ library }": "${ parsedJson.resolutions[library] }"`, `"${ library }": "${ newVersion }"`);
parsedJson = JSON.parse(content);
writeContent(file, content);
}
}
});
}
return [content, replaceResolution];
};
/**
* GitHub Actions updates
* Files: .github/workflows/**.yml
*
* Verify GitHub Actions use of current node version, e.g. node-version: '<18'
*/
const gitHubActionsUpdates = () => {
const files = glob.sync(params.paths || '.github/workflows/**.{yml,yaml}', { ignore });
files.forEach((file) => {
let content = fs.readFileSync(file, 'utf8');
const nodeVersionMatches = content.matchAll(/node-version: \'([0-9.x]+)\'/g);
const toReplace = [];
// Check all the node occurrences within the test file
if (nodeVersionMatches) {
for (const matches of nodeVersionMatches) {
for (const match of matches) {
const nodeVersion = semver.coerce(match);
if (nodeVersion && semver.lt(nodeVersion, semver.coerce(nodeRequirement))) {
content = content.replaceAll(`node-version: '${ match }'`, `node-version: '20.x'`);
writeContent(file, content);
toReplace.push([match, nodeRequirement]);
}
}
}
if (toReplace.length) {
printContent(file, `Updating node`, toReplace);
stats.githubActions.push(file);
stats.total.push(file);
}
}
});
};
/**
* NVM updates
* Files: .nvmrc
*
* Verify presence of .nvmrc, create one if none, update if any
*/
const nvmUpdates = () => {
const files = glob.sync(params.paths || '**/.nvmrc', { ignore });
const nvmRequirement = 20;
files.forEach((file) => {
if (file) {
let content = fs.readFileSync(file, 'utf8');
const nodeVersionMatch = content.match(/([0-9.x]+)/g);
const nodeVersion = semver.coerce(nodeVersionMatch[0]);
// Ensure node version is up to date
if (nodeVersion && semver.lt(nodeVersion, semver.coerce(nodeRequirement))) {
printContent(file, `Updating node ${ [nodeVersionMatch[0], nvmRequirement] }`);
content = content.replaceAll(nodeVersionMatch[0], nvmRequirement);
writeContent(file, content);
stats.nvmrc.push(file);
stats.total.push(file);
}
} else {
writeContent('.nvmrc', nvmRequirement);
}
});
};
/**
* Vue config update
* Files: vue.config.js
*
* Verify vue.config presence of deprecated Webpack5 options
* - devServer.public: 'path' -> client: { webSocketURL: 'path' }
*/
const vueConfigUpdates = () => {
const files = glob.sync(params.paths || 'vue.config**.js', { ignore });
files.forEach((file) => {
const content = fs.readFileSync(file, 'utf8');
// Verify vue.config presence of deprecated Webpack5 options
if (content.includes('devServer.public: \'path\'')) {
stats.webpack.push(file);
stats.total.push(file);
// TODO: Add replacement
}
});
};
/**
* Vue syntax update (to do not mix with tests)
* Files: .vue, .js, .ts (not .spec.ts, not .test.ts)
*/
const vueSyntaxUpdates = () => {
const files = glob.sync(params.paths || '**/*.{vue,js,ts}', { ignore: [...ignore, '**/*.spec.ts', '**/__tests__/**', '**/*.test.ts', 'jest.setup.js', '**/*.d.ts', '**/vue-shim.ts'] });
const replacementCases = [
// Prioritize set and delete to be converted since removed in Vue3
[/\=\> Vue\.set\((.*?),\s*(.*?),\s*(.*?)\)/g, (_, obj, prop, val) => `=> (${ obj.trim() }[${ prop.trim() }] = ${ val.trim() })`, 'removed and unnecessary due new reactivity https://vuejs.org/guide/extras/reactivity-in-depth.html'],
[/\=\> Vue\.set\((.*?),\s*'([^']*?)',\s*\{([\s\S]*?)\}\)/g, (_, obj, prop, val) => `=> (${ obj.trim() }['${ prop }'] = {${ val.trim() }})`, 'removed and unnecessary due new reactivity https://vuejs.org/guide/extras/reactivity-in-depth.html'],
[/\=\> Vue\.set\((.*?),\s*'([^']*?)',\s*(.*?)\)/g, (_, obj, prop, val) => `=> (${ obj.trim() }.${ prop } = ${ val.trim() })`, 'removed and unnecessary due new reactivity https://vuejs.org/guide/extras/reactivity-in-depth.html'],
[/Vue\.set\((.*?),\s*(.*?),\s*(.*?)\)/g, (_, obj, prop, val) => `${ obj.trim() }[${ prop.trim() }] = ${ val.trim() }`, 'removed and unnecessary due new reactivity https://vuejs.org/guide/extras/reactivity-in-depth.html'],
[/Vue\.set\((.*?),\s*'([^']*?)',\s*\{([\s\S]*?)\}\)/g, (_, obj, prop, val) => `${ obj.trim() }['${ prop }'] = {${ val.trim() }}`, 'removed and unnecessary due new reactivity https://vuejs.org/guide/extras/reactivity-in-depth.html'],
[/Vue\.set\((.*?),\s*'([^']*?)',\s*(.*?)\)/g, (_, obj, prop, val) => `${ obj.trim() }.${ prop } = ${ val.trim() }`, 'removed and unnecessary due new reactivity https://vuejs.org/guide/extras/reactivity-in-depth.html'],
[/Vue\.delete\((.*?),\s*(.*?)\)/g, (_, obj, prop) => `delete ${ obj.trim() }[${ prop.trim() }]`, 'removed and unnecessary due new reactivity https://vuejs.org/guide/extras/reactivity-in-depth.html'],
[/\=\> this\.\$set\((.*?),\s*(.*?),\s*(.*?)\)/g, (_, obj, prop, val) => `=> (${ obj.trim() }[${ prop.trim() }] = ${ val.trim() })`, 'removed and unnecessary due new reactivity https://vuejs.org/guide/extras/reactivity-in-depth.html'],
[/\=\> this\.\$set\((.*?),\s*'([^']*?)',\s*\{([\s\S]*?)\}\)/g, (_, obj, prop, val) => `=> (${ obj.trim() }['${ prop }'] = {${ val.trim() }})`, 'removed and unnecessary due new reactivity https://vuejs.org/guide/extras/reactivity-in-depth.html'],
[/\=\> this\.\$set\((.*?),\s*'([^']*?)',\s*(.*?)\)/g, (_, obj, prop, val) => `=> (${ obj.trim() }.${ prop } = ${ val.trim() })`, 'removed and unnecessary due new reactivity https://vuejs.org/guide/extras/reactivity-in-depth.html'],
[/this.\$set\((.*?),\s*'([^']*?)',\s*(.*?)\)/g, (_, obj, prop, val) => obj.trim() === 'this' ? `this['${ prop }'] = ${ val }` : `${ obj.trim() }['${ prop }'] = ${ val }`, 'removed and unnecessary due new reactivity https://vuejs.org/guide/extras/reactivity-in-depth.html'],
[/this\.\$set\((.*?),\s*(.*?),\s*(.*?)\)/g, (_, obj, prop, val) => `${ obj.trim() }[${ prop.trim() }] = ${ val.trim() }`, 'removed and unnecessary due new reactivity https://vuejs.org/guide/extras/reactivity-in-depth.html'],
[/this\.\$set\((.*?),\s*'([^']*?)',\s*\{([\s\S]*?)\}\)/g, (_, obj, prop, val) => `${ obj.trim() }['${ prop }'] = {${ val.trim() }}`, 'removed and unnecessary due new reactivity https://vuejs.org/guide/extras/reactivity-in-depth.html'],
[/this\.\$set\((.*?),\s*'([^']*?)',\s*(.*?)\)/g, (_, obj, prop, val) => `${ obj.trim() }.${ prop } = ${ val.trim() }`, 'removed and unnecessary due new reactivity https://vuejs.org/guide/extras/reactivity-in-depth.html'],
[/this\.\$delete\((.*?),\s*(.*?)\)/g, (_, obj, prop) => `delete ${ obj.trim() }[${ prop.trim() }]`, 'removed and unnecessary due new reactivity https://vuejs.org/guide/extras/reactivity-in-depth.html'],
// Replace imports for all the cases where createApp is needed, before the rest of the replacements
[/import Vue from 'vue';?/g, `import { createApp } from \'vue\';\nconst vueApp = createApp({});`, `https://v3-migration.vuejs.org/breaking-changes/global-api.html#a-new-global-api-createapp`],
[`new Vue(`, `createApp(`, `https://v3-migration.vuejs.org/breaking-changes/global-api.html#a-new-global-api-createapp`],
[`Vue.config`, `vueApp.config`, `https://v3-migration.vuejs.org/breaking-changes/global-api.html#a-new-global-api-createapp`],
[`Vue.directive`, `vueApp.directive`, `https://v3-migration.vuejs.org/breaking-changes/global-api.html#a-new-global-api-createapp`],
[`Vue.filter(`, `vueApp.filter(`, `https://v3-migration.vuejs.org/breaking-changes/global-api.html#a-new-global-api-createapp`],
[`Vue.mixin(`, `vueApp.mixin(`, `https://v3-migration.vuejs.org/breaking-changes/global-api.html#a-new-global-api-createapp`],
[`Vue.component(`, `vueApp.component(`, `https://v3-migration.vuejs.org/breaking-changes/global-api.html#a-new-global-api-createapp`],
[`Vue.use(`, `vueApp.use(`, `https://v3-migration.vuejs.org/breaking-changes/global-api.html#a-new-global-api-createapp`],
[`Vue.prototype`, `vueApp.config.globalProperties`, `https://v3-migration.vuejs.org/breaking-changes/global-api.html#a-new-global-api-createapp`],
['Vue.util', '', 'Vue.util is private and no longer available https://v3-migration.vuejs.org/migration-build.html#partially-compatible-with-caveats'],
// [`Vue.extend`, removePlaceholder, 'https://v3-migration.vuejs.org/breaking-changes/global-api.html#vue-extend-removed'],
// [`Vue.extend`, `createApp({})`], // (mixins)
[`vue-virtual-scroll-list`, `vue3-virtual-scroll-list`, 'library update'],
[`Vue.nextTick`, `nextTick`, 'https://v3-migration.vuejs.org/breaking-changes/global-api-treeshaking.html#global-api-treeshaking'],
[`this.nextTick`, `nextTick`, 'https://v3-migration.vuejs.org/breaking-changes/global-api-treeshaking.html#global-api-treeshaking'],
// TODO: Add missing import
[/( {4,}default)\(\)\s*\{([\s\S]*?)this\.([\s\S]*?\}\s*\})/g, (_, before, middle, after) => `${ before }(props) {${ middle }props.${ after }`, 'https://v3-migration.vuejs.org/breaking-changes/props-default-this.html'],
[`value=`, `modelValue=`],
[`@input=`, `@update:modelValue=`],
// [`v-bind.sync=`, `:modelValue=`, `https://v3-migration.vuejs.org/breaking-changes/v-model.html#using-v-bind-sync`],
// ['v-model=', ':modelValue=', ''],
[`click.native`, `click`, `https://v3-migration.vuejs.org/breaking-changes/v-model.html#using-v-bind-sync`],
[`v-on="$listeners"`, removePlaceholder, `removed and integrated with $attrs https://v3-migration.vuejs.org/breaking-changes/listeners-removed.html`],
[`:listeners="$listeners"`, `:v-bind="$attrs"`, `removed and integrated with $attrs https://v3-migration.vuejs.org/breaking-changes/listeners-removed.html`],
[/this\.\$scopedSlots\[(\w+)\]|this\.\$scopedSlots\.(\w+)/, (match, key1, key2) => `this.$slots.${ key1 || key2 }()`, `(many components loop through them) https://v3-migration.vuejs.org/breaking-changes/slots-unification.html`],
[` $scopedSlots`, ` $slots`, `(many components loop through them) https://v3-migration.vuejs.org/breaking-changes/slots-unification.html`],
[/slot="(\w+:\w+)"\s+slot-scope="(\w+)"/g, `$1="$2"`, `not mentioned in migration https://vuejs.org/guide/components/slots.html#scoped-slots`],
[/this\.\$slots\['([^']+)'\]/g, `this.$slots[\'$1\']()`, `not mentioned in migration https://vuejs.org/guide/components/slots.html#scoped-slots`],
// [/this\.\$slots\.([^']+)'/g, `this.$slots.$1()`, `not mentioned in migration https://vuejs.org/guide/components/slots.html#scoped-slots`],
// [/this\.\$slots(?!\s*\(\))(\b|\?|['"\[])/g, `this.$slots()$1`, `https://eslint.vuejs.org/rules/require-slots-as-functions.html`], // TODO: Add exception for existing brackets
// Portals are now Vue3 Teleports
[/<portal|<portal-target|<\/portal|<\/portal-target/g, '', `https://v3.vuejs.org/guide/teleport.html`],
// TODO: probably requires JSDom
// [/<template v-for="([\s\S]*?)">\s*<([\s\S]*?)\s*([\s\S]*?):key="([\s\S]*?)"([\s\S]*?)<\/([\s\S]*?)>\s*<\/template>/gs, '<template v-for="$1" :key="$4"><$3 $5>$6</$7>\n</template>', `https://v3-migration.vuejs.org/breaking-changes/key-attribute.html#with-template-v-for`],
// [/(<\w+(?!.*?v-for=)[^>]*?)\s*:key="[^"]*"\s*([^>]*>)/g, '$1$2', `https://v3-migration.vuejs.org/breaking-changes/key-attribute.html#with-template-v-for`],
[/(<\w+[^>v\-for]*?):key="[^"]*"\s*([^>]*>)/g, '$1$2', `https://v3-migration.vuejs.org/breaking-changes/key-attribute.html#with-template-v-for`],
[/(\<\w+\s+(?:[^>]*?\s+)?v-for="\(.*?,\s*(\w+)\s*\).*?")(?:\s*:key=".*"\s*)?([^>]*>)/g, '$1 :key="$2"$3', `https://v3-migration.vuejs.org/breaking-changes/key-attribute.html#with-template-v-for`],
[/(\<\w+\s+(?:[^>]*?\s+)?)v-for="(?!\*?\s+.*?\s+.*?)([^,]+?)\s+in\s+([^"]+?)"(?:\s*:key=".*"\s*)?([^>]*>)/g, '$1 v-for="($2, i) in $3" :key="i" $4', `https://v3-migration.vuejs.org/breaking-changes/key-attribute.html#with-template-v-for`],
// TODO: except for <components /> elements, probably requires JSDom
// [' is=', ``, `https://v3-migration.vuejs.org/breaking-changes/custom-elements-interop.html#customized-built-in-elements`],
// [' :is=', ``, `https://v3-migration.vuejs.org/breaking-changes/custom-elements-interop.html#customized-built-in-elements`],
// Directive updates
// [`bind(`, '', `beforeMount( but there's too many bind cases https://v3-migration.vuejs.org/breaking-changes/custom-directives.html`], // TODO: Restrict to directives and context
// [`update(`, '', `removed, also common term https://v3-migration.vuejs.org/breaking-changes/custom-directives.html`], // TODO: Restrict to directives and context
[`inserted(`, `mounted(`, 'https://v3-migration.vuejs.org/breaking-changes/custom-directives.html'],
[`componentUpdated(`, `updated(`, 'https://v3-migration.vuejs.org/breaking-changes/custom-directives.html'],
[`unbind`, `unmounted`, 'https://v3-migration.vuejs.org/breaking-changes/custom-directives.html'],
// [`propsData (app creation)`, ``, `use second argument of createApp({}) https://v3-migration.vuejs.org/breaking-changes/props-data.html`],
[`@hook:lifecycleHook`, `@vue:lifecycleHook`, `https://v3-migration.vuejs.org/breaking-changes/vnode-lifecycle-events.html`],
// Nuxt and initalize case only
// TODO: Use eventbus replacement as temporary solution?
[`$on('event', callback)`, '', `no migration, existing lib, https://v3-migration.vuejs.org/breaking-changes/events-api.html`],
[`$off('event', callback)`, '', `no migration, existing lib, https://v3-migration.vuejs.org/breaking-changes/events-api.html`],
[`$once('event', callback)`, '', `no migration, existing lib, https://v3-migration.vuejs.org/breaking-changes/events-api.html`],
// [`$children`, ``, `no migration, $refs are suggested as communication https://v3-migration.vuejs.org/breaking-changes/children.html`],
// Vuex
[`new Vuex.Store(`, `createStore(`, 'To install Vuex to a Vue instance, pass the store instead of Vuex https://vuex.vuejs.org/guide/migrating-to-4-0-from-3-x.html#installation-process'],
[`import Vuex from 'vuex'`, `import { createStore } from 'vuex'`, 'To install Vuex to a Vue instance, pass the store instead of Vuex https://vuex.vuejs.org/guide/migrating-to-4-0-from-3-x.html#installation-process'],
// Extra cases TBD (it seems like we already use the suggested way for arrays)
// watch option used on arrays not triggered by mutations https://v3-migration.vuejs.org/breaking-changes/watch.html
];
replaceCases('vueSyntax', files, replacementCases, `Updating Vue syntax`);
};
/**
* Vue Router
* Files: .vue, .js, .ts
*/
const routerUpdates = () => {
const files = glob.sync(params.paths || '**/*.{vue,js,ts}', { ignore });
const replacementCases = [
[`import Router from 'vue-router'`, `import { createRouter } from 'vue-router'`],
[`Vue.use(Router)`, `const router = createRouter({})`],
// [`currentRoute`, '', 'The currentRoute property is now a ref() https://router.vuejs.org/guide/migration/#The-currentRoute-property-is-now-a-ref-'],
[/import\s*\{([^}]*)\s* RouteConfig\s*([^}]*)\}\s*from\s*'vue-router'/g, (match, before, after) => `import {${ before.trim() } RouteRecordRaw ${ after.trim() }} from 'vue-router'`],
[/import\s*\{([^}]*)\s* Location\s*([^}]*)\}\s*from\s*'vue-router'/g, (match, before, after) => `import {${ before.trim() } RouteLocation ${ after.trim() }} from 'vue-router'`],
['imported Router', ''],
['router.name', '', 'now string | Symbol'],
[`mode: \'history\'`, 'history: createWebHistory()'],
// ['getMatchedComponents', '', 'https://router.vuejs.org/guide/migration/#Removal-of-router-getMatchedComponents-'],
];
replaceCases('router', files, replacementCases, `Updating Vue Router`);
};
/**
* Jest update
* https://test-utils.vuejs.org/migration
* Files: .spec.js, .spec.ts, .test.js, .test.ts
*/
// eslint-disable-next-line no-unused-vars
const jestUpdates = () => {
const files = glob.sync(params.paths || '**/*.{test.js,test.ts}', { ignore });
const cases = [
['config.mocks.$myGlobal', '', ''],
['createLocalVue', '', 'https://test-utils.vuejs.org/migration/#No-more-createLocalVue'],
['new Vuex.Store', '', ''],
['store', '', ''],
['propsData', 'props', 'https://test-utils.vuejs.org/migration/#propsData-is-now-props'],
['localVue.extend({})', '', ''],
['Vue.nextTick', '', ''],
['$destroy', '$unmount', 'https://test-utils.vuejs.org/migration/#destroy-is-now-unmount-to-match-Vue-3'],
['mocks', '', 'https://test-utils.vuejs.org/migration/#mocks-and-stubs-are-now-in-global'],
['stubs', '', 'https://test-utils.vuejs.org/migration/#mocks-and-stubs-are-now-in-global'],
['mixins', '', 'https://test-utils.vuejs.org/migration/#mocks-and-stubs-are-now-in-global'],
['plugins', '', 'https://test-utils.vuejs.org/migration/#mocks-and-stubs-are-now-in-global'],
['component', '', 'https://test-utils.vuejs.org/migration/#mocks-and-stubs-are-now-in-global'],
['directives', '', 'https://test-utils.vuejs.org/migration/#mocks-and-stubs-are-now-in-global'],
['slots', '', 'slotss scope is now exposed as params https://test-utils.vuejs.org/migration/#slots-s-scope-is-now-exposed-as-params'],
['scopedSlots', '', 'scopedSlots is now merged with slots https://test-utils.vuejs.org/migration/#scopedSlots-is-now-merged-with-slots'],
['parentComponent', '', 'deprecated '],
['contains', 'find', 'deprecated '],
['config.global.renderStubDefaultSlot = false', '', ''],
['findAll().at(0)', '', '']
];
replaceCases('jest', files, cases, `Updating Jest`);
};
/**
* Jest config updates
* Files: jest.config.js, .json, .ts
*
* /node_modules/@vue/vue2-jest --> reference needs new library version
*/
const jestConfigUpdates = () => {
const files = glob.sync(params.paths || '**/jest.config.{js,ts,json}', { ignore });
const cases = [
['/node_modules/@vue/vue2-jest', '/node_modules/@vue/vue3-jest']
];
replaceCases('jest', files, cases, `Updating Jest config`);
};
/**
* ESLint Updates
* Files: .eslintrc.js, .eslintrc.json, .eslintrc.yml
*/
const eslintUpdates = () => {
const files = glob.sync(params.paths || '**/.eslintrc.*{js,json,yml}', { ignore });
// Add cases introduced with new recommended settings
const replacePlugins = [
['plugin:vue/essential', 'plugin:vue/vue3-essential'],
['plugin:vue/strongly-recommended', 'plugin:vue/vue3-strongly-recommended'],
['plugin:vue/recommended', 'plugin:vue/vue3-recommended']
];
const newRules = {
'vue/one-component-per-file': 'off',
'vue/no-deprecated-slot-attribute': 'off',
'vue/require-explicit-emits': 'off',
'vue/v-on-event-hyphenation': 'off',
};
files.forEach((file) => {
let content = fs.readFileSync(file, 'utf8');
const matchedCases = [];
replacePlugins.forEach(([text, replacement]) => {
const isCase = content.includes(text);
if (isCase) {
content = content.replaceAll(text, replacement);
matchedCases.push([text, replacement]);
writeContent(file, content);
}
});
// Add the new rules if they don't exist
const eslintConfigPath = path.join(__dirname, `../${ file }`);
const eslintConfig = require(eslintConfigPath);
Object.keys(newRules).forEach((rule) => {
if (!eslintConfig.rules[rule]) {
eslintConfig.rules[rule] = newRules[rule];
matchedCases.push(rule);
}
});
writeContent(eslintConfigPath, `module.exports = ${ JSON.stringify(eslintConfig, null, 2) }`);
if (matchedCases.length) {
printContent(file, `Updating ESLint`, matchedCases);
stats.eslint.push(file);
stats.total.push(file);
}
});
};
/**
* TS Updates
* Files: tsconfig*.json
*
* Add information about TS issues, recommend @ts-nocheck as temporary solution
*/
const tsUpdates = () => {
console.warn('TS checks are stricter and may require to be fixed manually.',
'Use @ts-nocheck to give you time to fix them.',
'Add exception to your ESLint config to avoid linting errors.');
// TODO: Add case
};
/**
* Styles updates
*/
const stylesUpdates = () => {
const files = glob.sync(params.paths || '**/*.{vue, scss}', { ignore });
const cases = [
['::v-deep', ':deep()'],
];
replaceCases('style', files, cases, `Updating styles`);
};
/**
* Hook to write content
*/
const writeContent = (...args) => {
if (!isDry) {
fs.writeFileSync(...args);
}
};
/**
* Hook to print content
*/
const printContent = (...args) => {
if (isVerbose) {
console.log(...args);
}
};
/**
* Replace all cases for the provided files
*/
const replaceCases = (fileType, files, replacementCases, printText) => {
files.forEach((file) => {
const matchedCases = [];
let content = fs.readFileSync(file, 'utf8');
replacementCases.forEach(([text, replacement, notes]) => {
// Simple text
if (typeof text === 'string') {
if (content.includes(text)) {
// Exclude cases without replacement
if (replacement) {
// Remove discontinued functionalities which do not break
content = content.replaceAll(text, replacement === removePlaceholder ? '' : replacement);
}
if (!matchedCases.includes(`${ text }, ${ replacement }, ${ notes }`)) {
matchedCases.push(`${ text }, ${ replacement }, ${ notes }`);
}
}
} else {
// Regex case
// TODO: Fix issue not replacing all
// eslint-disable-next-line no-lonely-if
if (text.test(content) && replacement) {
content = content.replace(new RegExp(text, 'g'), replacement);
if (!matchedCases.includes(`${ text }, ${ replacement }, ${ notes }`)) {
matchedCases.push(`${ text }, ${ replacement }, ${ notes }`);
}
}
}
});
if (matchedCases.length) {
writeContent(file, content);
printContent(file, printText, matchedCases);
stats[fileType].push(file);
stats.total.push(file);
}
});
};
/**
* Print log
*/
const printLog = () => {
if (process.argv.includes('--files')) {
console.dir(stats, { compact: true });
}
const statsCount = Object.entries(stats).reduce((acc, [key, value]) => ({
...acc,
[key]: value.length
}), {});
console.table(statsCount);
if (process.argv.includes('--log')) {
fs.writeFileSync('stats.json', JSON.stringify(stats, null, 2));
}
};
const setParams = () => {
const args = process.argv.slice(2);
const paramKeys = ['paths'];
args.forEach((val) => {
paramKeys.forEach((key) => {
if (val.startsWith(`--${ key }=`)) {
params[key] = val.split('=')[1];
}
});
});
};
/**
* Init application
*/
(function() {
setParams();
packageUpdates();
gitHubActionsUpdates();
nvmUpdates();
vueConfigUpdates();
vueSyntaxUpdates();
routerUpdates();
// jestUpdates();
jestConfigUpdates();
eslintUpdates();
tsUpdates();
stylesUpdates();
printLog();
})();