Removing nuxt

This commit is contained in:
Cody Jackson 2022-10-07 11:03:46 -07:00 committed by Richard Cox
parent 7aade7ec4f
commit bbb7eebc90
64 changed files with 5544 additions and 940 deletions

View File

@ -12,6 +12,7 @@ dist-pkg
.DS_Store
shell/utils/dynamic-importer.js
ksconfig.json
nuxt
storybook-static/
utils/dynamic-importer.js
shell/assets/fonts

View File

@ -28,7 +28,7 @@ echo Installing dependencies
yarn install:ci
echo Building
NUXT_ENV_commit=$GITHUB_SHA NUXT_ENV_version=$GITHUB_REF_NAME OUTPUT_DIR="$ARTIFACT_LOCATION" ROUTER_BASE="$ROUTER_BASE" RANCHER_ENV=$RANCHER_ENV API=$API RESOURCE_BASE=$RESOURCE_BASE EXCLUDES_PKG=$EXCLUDES_PKG yarn run build --spa
COMMIT=$GITHUB_SHA VERSION=$GITHUB_REF_NAME OUTPUT_DIR="$ARTIFACT_LOCATION" ROUTER_BASE="$ROUTER_BASE" RANCHER_ENV=$RANCHER_ENV API=$API RESOURCE_BASE=$RESOURCE_BASE EXCLUDES_PKG=$EXCLUDES_PKG yarn run build --spa
echo Creating tar
tar -czf $RELEASE_LOCATION.tar.gz -C $ARTIFACT_LOCATION .

View File

@ -1 +1 @@
module.exports = { env: { test: { presets: [['@babel/env', { targets: { node: 'current' } }]] } } };
module.exports = require('./shell/babel.config.js');

View File

@ -6,7 +6,7 @@ export default class CheckboxInputPo extends ComponentPo {
return new CheckboxInputPo(
self
.find('.checkbox-outer-container')
.contains(`${ label } `)
.contains(label)
.parent()
);
}

View File

@ -6,7 +6,7 @@ export default class LabeledInputPo extends ComponentPo {
return new LabeledInputPo(
self
.find('.labeled-input', { includeShadowDom: true })
.contains(`${ label } `)
.contains(label)
.next()
);
}

View File

@ -1,8 +1,13 @@
{
"extends": "../tsconfig.default.json",
"extends": "../shell/tsconfig.default.json",
"compilerOptions": {
"noEmit": true,
"types": ["cypress"]
"types": [
"cypress"
]
},
"include": ["./**/*.ts", "../types/*.ts"]
"include": [
"./**/*.ts",
"../types/*.ts"
]
}

View File

@ -0,0 +1,33 @@
# Middleware
## Location
The definitions of middleware reside in `shell/middleware`. Middleware added to the object in `shell/nuxt/middleware.js` will be initialized at the start of the app rendering.
## Notes
This file was generated by nuxt and will soon be redefined by hand. It's safe to add new middleware to this file.
## Pattern
Define the middleware in a file that resides within `shell/middleware`. Then add the instantiation to the object that resides in `shell/nuxt/middleware.js`.
shell/middleware/i18n.js
```js
export default async function({
isHMR, app, store, route, params, error, redirect
}) {
// If middleware is called from hot module replacement, ignore it
if (isHMR) {
return;
}
await store.dispatch('i18n/init');
}
```
shell/nuxt/middleware.js
```js
...
middleware['i18n'] = require('../middleware/i18n.js')
middleware['i18n'] = middleware['i18n'].default || middleware['i18n']
...
```

View File

@ -0,0 +1,47 @@
# Nuxt Plugins
## Location
The definitions of plugins reside in `shell/plugins`. Plugins added to `shell/nuxt/index.js` will be initialized at the start of the app rendering.
## Notes
This file was generated by nuxt and will soon be redefined by hand. It's safe to add new plugins to this file.
## Pattern
Define the store in a file that resides within `shell/plugins`. Then add the plugins import and execution to `shell/nuxt/index.js`.
shell/plugins/version.js
```js
/**
* Fetch version metadata from backend /rancherversion API and store it
*
* This metadata does not change for an installation of Rancher
*/
import { setVersionData } from '@shell/config/version';
export default async function({ store }) {
try {
const response = await store.dispatch('rancher/request', {
url: '/rancherversion',
method: 'get',
redirectUnauthorized: false
});
setVersionData(response);
} catch (e) {
console.warn('Failed to fetch Rancher version metadata', e); // eslint-disable-line no-console
}
}
```
shell/nuxt/index.js
```js
...
import version from '../plugins/version';
...
if (process.client && typeof version === 'function') {
await version(app.context, inject);
}
...
```

View File

@ -0,0 +1,22 @@
# Routes
## Location
The core dashboard routes are defined in `shell/nuxt/router.js`.
## Notes
This file was generated by nuxt and will soon be redefined by hand. It's safe to add new routes to this file.
## Pattern
First instantiate a page component at the top of the file. Then define a new route at the bottom of the file by giving the page component a unique path and name
```js
const about = () => interopDefault(import('../pages/about.vue'))
...
{
path: "/about",
component: about,
name: "about"
}
...
```

View File

@ -0,0 +1,18 @@
# Stores
## Location
The definitions of stores reside in `shell/store`. Stores added to `shell/nuxt/store.js` will be initialized at the start of the app rendering.
## Notes
This file was generated by nuxt and will soon be redefined by hand. It's safe to add new stores to this file.
## Pattern
Define the store in a file that resides within `shell/store`. Then add the store to `shell/nuxt/store.js`.
shell/nuxt/store.js
```js
...
resolveStoreModules(require('../store/i18n.js'), 'i18n.js')
...
```

View File

@ -55,6 +55,10 @@ const sidebars = {
'code-base-works/helm-chart-apps',
'code-base-works/keyboard-shortcuts',
'code-base-works/kubernetes-resources-data-load',
'code-base-works/routes',
'code-base-works/middleware',
'code-base-works/stores',
'code-base-works/nuxt-plugins',
'code-base-works/machine-drivers',
'code-base-works/performance',
'code-base-works/sortable-table',

View File

@ -1,14 +0,0 @@
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"baseUrl": ".",
"module": "commonjs",
"paths": {
"@/*": ["*"]
},
"sourceMap": true,
"target": "es2017"
},
"exclude": ["node_modules", "dist","tmp"]
}

View File

@ -13,7 +13,7 @@
"pkg/rancher-components"
],
"scripts": {
"build-pkg": "./shell/scripts/build-pkg.sh",
"build-pkg": "yarn lint && ./shell/scripts/build-pkg.sh",
"publish-pkg": "./shell/scripts/publish-pkg.sh",
"serve-pkgs": "./shell/scripts/serve-pkgs",
"publish-shell": "./shell/scripts/publish-shell.sh",
@ -24,20 +24,19 @@
"test": "jest --watch",
"test:ci": "jest --collectCoverage",
"install:ci": "yarn install --frozen-lockfile",
"nuxt": "./node_modules/.bin/nuxt",
"dev": "source ./scripts/version && ./node_modules/.bin/nuxt dev",
"mem-dev": "source ./scripts/version && node --max-old-space-size=8192 ./node_modules/.bin/nuxt dev",
"dev": "source ./scripts/version && NODE_ENV=dev ./node_modules/.bin/vue-cli-service serve",
"mem-dev": "source ./scripts/version && NODE_ENV=dev node --max-old-space-size=8192 ./node_modules/.bin/vue-cli-service serve",
"docker-dev": "docker run --rm --name dashboard-dev -p 8005:8005 -e API=$API -v $(pwd):/src -v dashboard_node:/src/node_modules rancher/dashboard:dev",
"docker:local:start": "docker run -d --restart=unless-stopped -p 80:80 -p 443:443 -e CATTLE_BOOTSTRAP_PASSWORD=password -e CATTLE_PASSWORD_MIN_LENGTH=3 --name cypress --privileged rancher/rancher:v2.7-head",
"docker:local:stop": "docker kill cypress || docker rm cypress || true",
"build": "./node_modules/.bin/nuxt build --devtools",
"build": "yarn lint && ./node_modules/.bin/vue-cli-service build",
"build:lib": "cd pkg/rancher-components && yarn build:lib",
"analyze": "./node_modules/.bin/nuxt build --analyze",
"start": "./node_modules/.bin/nuxt start",
"analyze": "./node_modules/.bin/vue-cli-service build --report",
"start": "./node_modules/.bin/vue-cli-service serve",
"start:dev": "NODE_ENV=dev yarn start",
"start:prod": "DEV_PORTS=true NODE_ENV=production yarn start",
"generate": "./node_modules/.bin/nuxt generate",
"dev-debug": "node --inspect ./node_modules/.bin/nuxt",
"start:prod": "NODE_OPTIONS=--max_old_space_size=4096 DEV_PORTS=true NODE_ENV=production yarn start",
"generate": "yarn build",
"dev-debug": "node --inspect ./node_modules/.bin/vue-cli-service",
"cy:e2e": "cypress open --e2e --browser chrome",
"cy:open": "cypress open",
"cy:run": "cypress run --browser chrome",
@ -109,12 +108,18 @@
"shell-quote": "1.7.3",
"sinon": "8.1.1",
"ts-node": "8.10.2",
"ufo": "0.7.11",
"unfetch": "4.2.0",
"url-parse": "1.5.10",
"v-tooltip": "2.0.3",
"vue-client-only": "2.1.0",
"vue-clipboard2": "0.3.1",
"vue-codemirror": "4.0.6",
"vue-js-modal": "1.3.35",
"vue-meta": "2.4.0",
"vue-no-ssr": "1.1.1",
"vue-resize": "0.4.5",
"vue-router": "3.6.5",
"vue-select": "3.18.3",
"vue-server-renderer": "2.6.14",
"vue-shortkey": "3.1.7",
@ -141,14 +146,13 @@
"@nuxtjs/eslint-config-typescript": "6.0.1",
"@nuxtjs/eslint-module": "1.2.0",
"@nuxtjs/style-resources": "1.2.1",
"@types/copy-webpack-plugin": "^5.0.3",
"@types/jest": "27.4.1",
"@types/lodash": "4.14.184",
"@types/node": "16.4.3",
"@types/vue-select": "3.16.0",
"@typescript-eslint/eslint-plugin": "4.33.0",
"@typescript-eslint/parser": "4.33.0",
"@vue/cli-plugin-babel": "4.5.15",
"@vue/cli-plugin-typescript": "4.5.15",
"@vue/cli-service": "4.5.15",
"@vue/eslint-config-standard": "5.1.2",
"@vue/test-utils": "1.2.1",

View File

@ -10,8 +10,7 @@
"registry": "http://localhost:4873"
},
"scripts": {
"dev": "./node_modules/.bin/nuxt dev",
"nuxt": "./node_modules/.bin/nuxt"
"dev": "./node_modules/.bin/vue-cli-service dev"
},
"engines": {
"node": ">=14"

View File

@ -21,7 +21,7 @@ DIR=${GIT_TAG:-$COMMIT_BRANCH}
OUTPUT_DIR=dist/${DIR}-embedded
echo "Building..."
NUXT_ENV_commit=${COMMIT} NUXT_ENV_version=${VERSION} OUTPUT_DIR=$OUTPUT_DIR ROUTER_BASE='/dashboard' yarn run build
COMMIT=${COMMIT} VERSION=${VERSION} OUTPUT_DIR=$OUTPUT_DIR ROUTER_BASE='/dashboard' yarn run build
if [ -v EMBED_PKG ]; then
echo "Build and embed plugin from: $EMBED_PKG"

View File

@ -25,4 +25,4 @@ BASE=${BASE:-https://releases.rancher.com/dashboard/${DIR}}
echo "Building for ${BASE}..."
NUXT_ENV_commit=${COMMIT} NUXT_ENV_version=${VERSION} OUTPUT_DIR=dist/${DIR} ROUTER_BASE="/dashboard" RESOURCE_BASE="${BASE}" yarn run build --spa
COMMIT=${COMMIT} VERSION=${VERSION} OUTPUT_DIR=dist/${DIR} ROUTER_BASE="/dashboard" RESOURCE_BASE="${BASE}" yarn run build

View File

@ -33,4 +33,4 @@
@import "./vendor/vue-select";
@import "./vendor/vue-js-modal";
@import "./vendor/code-mirror";
@import '@/node_modules/xterm/css/xterm.css';
@import 'node_modules/xterm/css/xterm.css';

View File

@ -4,8 +4,8 @@
font-style: normal;
font-weight: normal;
src: local(''),
url('~shell/assets/fonts/poppins/poppins-v15-latin-300.woff2') format('woff2'), /* Super Modern Browsers */
url('~shell/assets/fonts/poppins/poppins-v15-latin-300.woff') format('woff'), /* Modern Browsers */
url('~@shell/assets/fonts/poppins/poppins-v15-latin-300.woff2') format('woff2'), /* Super Modern Browsers */
url('~@shell/assets/fonts/poppins/poppins-v15-latin-300.woff') format('woff'), /* Modern Browsers */
}
/* poppins-500 - latin */
@ -14,8 +14,8 @@
font-style: normal;
font-weight: bold;
src: local(''),
url('~shell/assets/fonts/poppins/poppins-v15-latin-500.woff2') format('woff2'), /* Super Modern Browsers */
url('~shell/assets/fonts/poppins/poppins-v15-latin-500.woff') format('woff'), /* Modern Browsers */
url('~@shell/assets/fonts/poppins/poppins-v15-latin-500.woff2') format('woff2'), /* Super Modern Browsers */
url('~@shell/assets/fonts/poppins/poppins-v15-latin-500.woff') format('woff'), /* Modern Browsers */
}
/* lato-regular - latin */
@ -24,8 +24,8 @@
font-style: normal;
font-weight: normal;
src: local(''),
url('~shell/assets/fonts/lato/lato-v17-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
url('~shell/assets/fonts/lato/lato-v17-latin-regular.woff') format('woff'), /* Modern Browsers */
url('~@shell/assets/fonts/lato/lato-v17-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
url('~@shell/assets/fonts/lato/lato-v17-latin-regular.woff') format('woff'), /* Modern Browsers */
}
/* lato-700 - latin */
@ -34,8 +34,8 @@
font-style: normal;
font-weight: bold;
src: local(''),
url('~shell/assets/fonts/lato/lato-v17-latin-700.woff2') format('woff2'), /* Super Modern Browsers */
url('~shell/assets/fonts/lato/lato-v17-latin-700.woff') format('woff'), /* Modern Browsers */
url('~@shell/assets/fonts/lato/lato-v17-latin-700.woff2') format('woff2'), /* Super Modern Browsers */
url('~@shell/assets/fonts/lato/lato-v17-latin-700.woff') format('woff'), /* Modern Browsers */
}
/* roboto-mono-regular - latin */
@ -44,6 +44,6 @@
font-style: normal;
font-weight: normal;
src: local(''),
url('~shell/assets/fonts/roboto-mono/roboto-mono-v13-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
url('~shell/assets/fonts/roboto-mono/roboto-mono-v13-latin-regular.woff') format('woff'), /* Modern Browsers */
}
url('~@shell/assets/fonts/roboto-mono/roboto-mono-v13-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
url('~@shell/assets/fonts/roboto-mono/roboto-mono-v13-latin-regular.woff') format('woff'), /* Modern Browsers */
}

View File

@ -1,4 +1,4 @@
@import '~/node_modules/vue-js-modal/dist/styles.css';
@import 'node_modules/vue-js-modal/dist/styles.css';
.v--modal-overlay {
background-color: var(--overlay-bg);
@ -10,7 +10,7 @@
}
.v--modal {
background-color: var(--modal-bg)!important;
background-color: var(--modal-bg) !important;
box-shadow: none;
border: 2px solid var(--modal-border);
}
}

13
shell/babel.config.js Normal file
View File

@ -0,0 +1,13 @@
module.exports = {
presets: [
[
'@vue/cli-plugin-babel/preset',
{ useBuiltIns: false }
],
[
'@babel/preset-env',
{ targets: { node: 'current' } }
]
],
env: { test: { presets: [['@babel/env', { targets: { node: 'current' } }]] } }
};

View File

@ -1,18 +1 @@
module.exports = {
env: {
test: {
plugins: [
[
'module-resolver',
{
root: ['.'],
alias: {
'@': '.',
'~': '.',
},
},
],
],
},
},
};
module.exports = require('@rancher/shell/babel.config.js');

View File

@ -1,6 +0,0 @@
import config from '@rancher/shell/nuxt.config';
export default config(__dirname, {
excludes: [],
autoImport: []
});

View File

@ -0,0 +1,10 @@
/**
* This file is here purely to support using the typescript version of the vue config vue.config.ts.
*/
require('ts-node').register({
project: './tsconfig.json',
compilerOptions: { module: 'commonjs' },
logError: true
});
module.exports = require('./vue.config.ts').default;

View File

@ -0,0 +1,7 @@
import config from '@rancher/shell/vue.config';
export default config(__dirname, {
excludes: [],
// excludes: ['fleet', 'example']
// autoLoad: ['fleet', 'example']
});

View File

@ -4,15 +4,15 @@ const path = require('path');
const fs = require('fs-extra');
const targets = {
dev: './node_modules/.bin/nuxt dev',
nuxt: './node_modules/.bin/nuxt',
build: './node_modules/.bin/nuxt build',
dev: 'NODE_ENV=dev ./node_modules/.bin/vue-cli-service serve',
build: './node_modules/.bin/vue-cli-service build',
clean: './node_modules/@rancher/shell/scripts/clean'
};
const files = [
'tsconfig.json',
'nuxt.config.js',
'vue.config.js',
'vue.config.ts',
'.eslintignore',
'.eslintrc.js',
'babel.config.js',
@ -25,7 +25,7 @@ console.log('Creating Skeleton Application');
const args = process.argv;
let appFolder = path.resolve('.');
if (args.length == 3) {
if (args.length === 3) {
const name = args[2];
const folder = path.resolve('.');

View File

@ -1,799 +0,0 @@
import fs from 'fs';
import path from 'path';
import serveStatic from 'serve-static';
import webpack from 'webpack';
import { STANDARD } from './config/private-label';
import { generateDynamicTypeImport } from './pkg/auto-import';
import { directiveSsr as t } from './plugins/i18n';
import { trimWhitespaceSsr as trimWhitespace } from './plugins/trim-whitespace';
const createProxyMiddleware = require('http-proxy-middleware');
// Global variables
let api = process.env.API || 'http://localhost:8989';
if ( !api.startsWith('http') ) {
api = `https://${ api }`;
}
// needed for proxies
export const API_PATH = api;
const dev = (process.env.NODE_ENV !== 'production');
const devPorts = dev || process.env.DEV_PORTS === 'true';
// human readable version used on rancher dashboard about page
const dashboardVersion = process.env.DASHBOARD_VERSION;
const prime = process.env.PRIME;
const pl = process.env.PL || STANDARD;
const perfTest = (process.env.PERF_TEST === 'true'); // Enable performance testing when in dev
const instrumentCode = (process.env.TEST_INSTRUMENT === 'true'); // Instrument code for code coverage in e2e tests
// Allow skipping of eslint check
// 0 = Skip browser and console checks
// 1 = Skip browser check
// 2 = Do not skip any checks
const skipEsLintCheckStr = (process.env.SKIP_ESLINT || '');
let skipEsLintCheck = parseInt(skipEsLintCheckStr, 10) || 2;
// ===============================================================================================
// Nuxt configuration
// ===============================================================================================
// Expose a function that can be used by an app to provide a nuxt configuration for building an application
// This takes the directory of the application as tehfirst argument so that we can derive folder locations
// from it, rather than from the location of this file
export default function(dir, _appConfig) {
// Paths to the shell folder when it is included as a node dependency
let SHELL = 'node_modules/@rancher/shell';
let SHELL_ABS = path.join(dir, 'node_modules/@rancher/shell');
let NUXT_SHELL = '~~node_modules/@rancher/shell';
let COMPONENTS_DIR = path.join(SHELL_ABS, 'rancher-components');
let typescript = {};
if (fs.existsSync(SHELL_ABS)) {
const stat = fs.lstatSync(SHELL_ABS);
// If @rancher/shell is a symlink, then use the components folder for it
if (stat.isSymbolicLink()) {
const REAL_SHELL_ABS = fs.realpathSync(SHELL_ABS); // In case the shell is being linked via 'yarn link'
COMPONENTS_DIR = path.join(REAL_SHELL_ABS, '..', 'pkg', 'rancher-components', 'src', 'components');
// For now, skip eslint check when being linked via yarn link - pkg folder is linked otherwise
// This will change when we remove nuxt
skipEsLintCheck = true;
}
}
// If we have a local folder named 'shell' then use that rather than the one in node_modules
// This will be the case in the main dashboard repository.
if (fs.existsSync(path.join(dir, 'shell'))) {
SHELL = './shell';
SHELL_ABS = path.join(dir, 'shell');
NUXT_SHELL = '~~/shell';
COMPONENTS_DIR = path.join(dir, 'pkg', 'rancher-components', 'src', 'components');
// Skip eslint check that runs as part of nuxt build in the console
if (skipEsLintCheck > 0) {
typescript = { typeCheck: { eslint: { files: './shell/**/*.{ts,js,vue}' } } };
}
}
// Instrument code for tests
const babelPlugins = [
// TODO: Browser support
// ['@babel/plugin-transform-modules-commonjs'],
['@babel/plugin-proposal-private-property-in-object', { loose: true }]
];
if (instrumentCode) {
babelPlugins.push('babel-plugin-istanbul');
console.warn('Instrumenting code for coverage'); // eslint-disable-line no-console
}
// ===============================================================================================
// Functions for the UI Plugins
// ===============================================================================================
const appConfig = _appConfig || {};
const excludes = appConfig.excludes || [];
const autoLoad = appConfig.autoLoad || [];
const serverMiddleware = [];
const autoLoadPackages = [];
const watcherIgnores = [
/.shell/,
/dist-pkg/,
/scripts\/standalone/
];
autoLoad.forEach((pkg) => {
// Need the version number of each file
const pkgPackageFile = require(path.join(dir, 'pkg', pkg, 'package.json'));
const pkgRef = `${ pkg }-${ pkgPackageFile.version }`;
autoLoadPackages.push({
name: `app-autoload-${ pkgRef }`,
content: `/pkg/${ pkgRef }/${ pkgRef }.umd.min.js`
});
// Anything auto-loaded should also be excluded
if (!excludes.includes(pkg)) {
excludes.push(pkg);
}
});
// Find any UI packages in node_modules
const NM = path.join(dir, 'node_modules');
const pkg = require(path.join(dir, 'package.json'));
const nmPackages = {};
if (pkg && pkg.dependencies) {
Object.keys(pkg.dependencies).forEach((pkg) => {
const f = require(path.join(NM, pkg, 'package.json'));
// The package.json must have the 'rancher' property to mark it as a UI package
if (f.rancher) {
const id = `${ f.name }-${ f.version }`;
nmPackages[id] = f.main;
// Add server middleware to serve up the files for this UI package
serverMiddleware.push({
path: `/pkg/${ id }`,
handler: serveStatic(path.join(NM, pkg))
});
}
});
}
serverMiddleware.push({
path: '/uiplugins-catalog',
handler: (req, res, next) => {
const p = req.url.split('?');
try {
const proxy = createProxyMiddleware({
target: p[1],
pathRewrite: { '^.*': p[0] }
});
return proxy(req, res, next);
} catch (e) {
console.error(e); // eslint-disable-line no-console
}
}
});
function includePkg(name) {
if (name.startsWith('.') || name === 'node_modules') {
return false;
}
return !excludes || (excludes && !excludes.includes(name));
}
excludes.forEach((e) => {
watcherIgnores.push(new RegExp(`/pkg.${ e }`));
});
// For each package in the pkg folder that is being compiled into the application,
// Add in the code to automatically import the types from that package
// This imports models, edit, detail, list etc
// When built as a UI package, shell/pkg/vue.config.js does the same thing
const autoImportTypes = {};
const VirtualModulesPlugin = require('webpack-virtual-modules');
let reqs = '';
const pkgFolder = path.relative(dir, './pkg');
if (fs.existsSync(pkgFolder)) {
const items = fs.readdirSync(path.relative(dir, './pkg'));
// Ignore hidden folders
items.filter(name => !name.startsWith('.')).forEach((name) => {
const f = require(path.join(dir, 'pkg', name, 'package.json'));
// Package file must have rancher field to be a plugin
if (includePkg(name) && f.rancher) {
reqs += `$plugin.initPlugin('${ name }', require(\'~/pkg/${ name }\')); `;
// // Serve the code for the UI package in case its used for dynamic loading (but not if the same package was provided in node_modules)
// if (!nmPackages[name]) {
// const pkgPackageFile = require(path.join(dir, 'pkg', name, 'package.json'));
// const pkgRef = `${ name }-${ pkgPackageFile.version }`;
// serverMiddleware.push({ path: `/pkg/${ pkgRef }`, handler: serveStatic(`${ dir }/dist-pkg/${ pkgRef }`) });
// }
autoImportTypes[`@rancher/auto-import/${ name }`] = generateDynamicTypeImport(`@pkg/${ name }`, path.join(dir, `pkg/${ name }`));
}
});
}
Object.keys(nmPackages).forEach((m) => {
reqs += `$plugin.loadAsync('${ m }', '/pkg/${ m }/${ nmPackages[m] }');`;
});
// Generate a virtual module '@rancher/dyanmic.js` which imports all of the packages that should be built into the application
// This is imported in 'shell/extensions/extension-loader.js` which ensures the all code for plugins to be included is imported in the application
const virtualModules = new VirtualModulesPlugin({ 'node_modules/@rancher/dynamic.js': `export default function ($plugin) { ${ reqs } };` });
const autoImport = new webpack.NormalModuleReplacementPlugin(/^@rancher\/auto-import$/, (resource) => {
const ctx = resource.context.split('/');
const pkg = ctx[ctx.length - 1];
resource.request = `@rancher/auto-import/${ pkg }`;
});
// @pkg imports must be resolved to the package that it importing them - this allows a package to use @pkg as an alis
// to the root of that particular package
const pkgImport = new webpack.NormalModuleReplacementPlugin(/^@pkg/, (resource) => {
const ctx = resource.context.split('/');
// Find 'pkg' folder in the contxt
const index = ctx.findIndex(s => s === 'pkg');
if (index !== -1 && (index + 1) < ctx.length) {
const pkg = ctx[index + 1];
let p = path.resolve(dir, 'pkg', pkg, resource.request.substr(5));
if (resource.request.startsWith(`@pkg/${ pkg }`)) {
p = path.resolve(dir, 'pkg', resource.request.substr(5));
}
resource.request = p;
}
});
// Serve up the dist-pkg folder under /pkg
serverMiddleware.push({ path: `/pkg/`, handler: serveStatic(`${ dir }/dist-pkg/`) });
// Add the standard dashboard server middleware after the middleware added to serve up UI packages
serverMiddleware.push(path.resolve(dir, SHELL, 'server', 'server-middleware'));
// ===============================================================================================
// Dashboard nuxt configuration
// ===============================================================================================
require('events').EventEmitter.defaultMaxListeners = 20;
require('dotenv').config();
let routerBasePath = '/';
let resourceBase = '';
let outputDir = 'dist';
if ( typeof process.env.ROUTER_BASE !== 'undefined' ) {
routerBasePath = process.env.ROUTER_BASE;
}
if ( typeof process.env.RESOURCE_BASE !== 'undefined' ) {
resourceBase = process.env.RESOURCE_BASE;
}
if ( typeof process.env.OUTPUT_DIR !== 'undefined' ) {
outputDir = process.env.OUTPUT_DIR;
}
if ( resourceBase && !resourceBase.endsWith('/') ) {
resourceBase += '/';
}
console.log(`Build: ${ dev ? 'Development' : 'Production' }`); // eslint-disable-line no-console
if ( !dev ) {
console.log(`Version: ${ dashboardVersion }`); // eslint-disable-line no-console
}
if ( resourceBase ) {
console.log(`Resource Base URL: ${ resourceBase }`); // eslint-disable-line no-console
}
if ( routerBasePath !== '/' ) {
console.log(`Router Base Path: ${ routerBasePath }`); // eslint-disable-line no-console
}
if ( pl !== STANDARD ) {
console.log(`PL: ${ pl }`); // eslint-disable-line no-console
}
const rancherEnv = process.env.RANCHER_ENV || 'web';
console.log(`API: '${ api }'. Env: '${ rancherEnv }'`); // eslint-disable-line no-console
// Nuxt modules
let nuxtModules = [
'@nuxtjs/proxy',
'@nuxtjs/axios',
'@nuxtjs/eslint-module',
'@nuxtjs/webpack-profile',
'cookie-universal-nuxt',
'portal-vue/nuxt',
path.join(NUXT_SHELL, 'plugins/dashboard-store/rehydrate-all'),
];
// Remove es-lint nuxt module if env var configures this
if (skipEsLintCheck < 2) {
nuxtModules = nuxtModules.filter(s => !s.includes('eslint-module'));
}
const config = {
dev,
// Configuration visible to the client, https://nuxtjs.org/api/configuration-env
env: {
dev,
pl,
perfTest,
rancherEnv,
harvesterPkgUrl: process.env.HARVESTER_PKG_URL,
api
},
// vars accessible via this.$config https://nuxtjs.org/docs/configuration-glossary/configuration-runtime-config/
publicRuntimeConfig: { rancherEnv, dashboardVersion },
buildDir: dev ? '.nuxt' : '.nuxt-prod',
buildModules: [
'@nuxtjs/style-resources',
'@nuxt/typescript-build'
],
styleResources: {
// only import functions, mixins, or variables, NEVER import full styles https://github.com/nuxt-community/style-resources-module#warning
hoistUseStatements: true,
scss: [
path.resolve(SHELL_ABS, 'assets/styles/base/_variables.scss'),
path.resolve(SHELL_ABS, 'assets/styles/base/_functions.scss'),
path.resolve(SHELL_ABS, 'assets/styles/base/_mixins.scss'),
],
},
loadingIndicator: path.join(SHELL_ABS, 'static/loading-indicator.html'),
loading: path.join(SHELL_ABS, 'components/nav/GlobalLoading.vue'),
// Axios: https://axios.nuxtjs.org/options
axios: {
https: true,
proxy: true,
retry: { retries: 0 },
// debug: true
},
content: {
dir: path.resolve(SHELL_ABS, 'content'),
markdown: { prism: { theme: false } },
liveEdit: false
},
router: {
base: routerBasePath,
middleware: ['i18n'],
prefetchLinks: false
},
alias: {
'~shell': SHELL_ABS,
'@shell': SHELL_ABS,
'@pkg': path.join(dir, 'pkg'),
'@components': COMPONENTS_DIR,
},
modulesDir: [
path.resolve(dir),
'./node_modules',
SHELL_ABS
],
dir: {
assets: path.posix.join(SHELL, 'assets'),
layouts: path.posix.join(SHELL, 'layouts'),
middleware: path.posix.join(SHELL, 'middleware'),
pages: path.posix.join(SHELL, 'pages'),
static: path.posix.join(SHELL, 'static'),
store: path.posix.join(SHELL, 'store'),
},
watchers: { webpack: { ignore: watcherIgnores } },
build: {
publicPath: resourceBase,
parallel: true,
cache: true,
hardSource: true,
// Uses the Webpack Build Analyzer to generate a report of the bundle contents
// analyze: { analyzerMode: 'static' },
uglify: {
uglifyOptions: { compress: !dev },
cache: './node_modules/.cache/uglify'
},
'html.minify': {
collapseBooleanAttributes: !dev,
decodeEntities: !dev,
minifyCSS: !dev,
minifyJS: !dev,
processConditionalComments: !dev,
removeEmptyAttributes: !dev,
removeRedundantAttributes: !dev,
trimCustomFragments: !dev,
useShortDoctype: !dev
},
// Don't include `[name]` in prod file names
// This flattens out the folder structure (avoids crazy paths like `_nuxt/pages/account/create-key/pages/c/_cluster/_product/_resource/_id/pages/c/_cluster/_product/_resource`)
// and uses nuxt's workaround to address issues with filenames containing `//` (see https://github.com/nuxt/nuxt.js/issues/8274)
filenames: { chunk: ({ isDev }) => isDev ? '[name].js' : '[contenthash].js' },
// @TODO figure out how to split chunks up better, by product
// optimization: {
// splitChunks: {
// cacheGroups: {
// styles: {
// name: 'styles',
// test: /\.(css|vue)$/,
// chunks: 'all',
// enforce: true
// },
// }
// }
// },
plugins: [
virtualModules,
autoImport,
new VirtualModulesPlugin(autoImportTypes),
pkgImport,
],
extend(config, { isClient, isDev }) {
if ( isDev ) {
config.devtool = 'cheap-module-source-map';
} else {
config.devtool = 'source-map';
}
if ( resourceBase ) {
config.output.publicPath = resourceBase;
}
// Remove default image handling rules
for ( let i = config.module.rules.length - 1 ; i >= 0 ; i-- ) {
if ( /svg/.test(config.module.rules[i].test) ) {
config.module.rules.splice(i, 1);
}
}
config.resolve.symlinks = false;
// Ensure we process files in the @rancher/shell folder
config.module.rules.forEach((r) => {
if ('test.js'.match(r.test)) {
if (r.exclude) {
const orig = r.exclude;
r.exclude = function(modulePath) {
if (modulePath.indexOf(SHELL_ABS) === 0) {
return false;
}
return orig(modulePath);
};
}
}
});
// And substitute our own loader for images
config.module.rules.unshift({
test: /\.(png|jpe?g|gif|svg|webp)$/,
use: [
{
loader: 'url-loader',
options: {
name: '[path][name].[ext]',
limit: 1,
esModule: false
},
}
]
});
// Handler for yaml files (used for i18n files, for example)
config.module.rules.unshift({
test: /\.ya?ml$/i,
loader: 'js-yaml-loader',
options: { name: '[path][name].[ext]' },
});
// Handler for csv files (e.g. ec2 instance data)
config.module.rules.unshift({
test: /\.csv$/i,
loader: 'csv-loader',
options: {
dynamicTyping: true,
header: true,
skipEmptyLines: true
},
});
// Ensure there is a fallback for browsers that don't support web workers
config.module.rules.unshift({
test: /web-worker.[a-z-]+.js/i,
loader: 'worker-loader',
options: { inline: 'fallback' },
});
// Prevent warning in log with the md files in the content folder
config.module.rules.push({
test: /\.md$/,
use: [
{
loader: 'frontmatter-markdown-loader',
options: { mode: ['body'] }
}
]
});
},
// extractCSS: true,
cssSourceMap: true,
babel: {
presets({ isServer }) {
return [
[
require.resolve('@nuxt/babel-preset-app'),
{
// buildTarget: isServer ? 'server' : 'client',
corejs: { version: 3 },
targets: isServer ? { node: '12' } : { browsers: ['last 2 versions'] },
modern: !isServer
}
],
'@babel/preset-typescript',
];
},
plugins: babelPlugins
}
},
render: {
bundleRenderer: {
directives: {
trimWhitespace,
t,
}
}
},
// modern: true, -- now part of preset above
generate: { dir: outputDir },
// Global CSS
css: [
path.resolve(SHELL_ABS, 'assets/styles/app.scss')
],
head: {
title: process.env.npm_package_name || '',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{
hid: 'description',
name: 'description',
content: process.env.npm_package_description || ''
},
...autoLoadPackages,
],
link: [{
hid: 'icon',
rel: 'icon',
type: 'image/x-icon',
href: `${ resourceBase || '/' }favicon.png`
}]
},
// Nuxt modules
modules: nuxtModules,
// Vue plugins
plugins: [
// Extensions
path.relative(dir, path.join(SHELL, 'core/plugins.js')),
path.relative(dir, path.join(SHELL, 'core/plugins-loader.js')), // Load builtin plugins
// Third-party
path.join(NUXT_SHELL, 'plugins/axios'),
path.join(NUXT_SHELL, 'plugins/tooltip'),
path.join(NUXT_SHELL, 'plugins/vue-clipboard2'),
path.join(NUXT_SHELL, 'plugins/v-select'),
path.join(NUXT_SHELL, 'plugins/directives'),
path.join(NUXT_SHELL, 'plugins/transitions'),
{ src: path.join(NUXT_SHELL, 'plugins/vue-js-modal') },
{ src: path.join(NUXT_SHELL, 'plugins/js-yaml'), ssr: false },
{ src: path.join(NUXT_SHELL, 'plugins/resize'), ssr: false },
{ src: path.join(NUXT_SHELL, 'plugins/shortkey'), ssr: false },
// First-party
path.join(NUXT_SHELL, 'plugins/i18n'),
path.join(NUXT_SHELL, 'plugins/global-formatters'),
path.join(NUXT_SHELL, 'plugins/trim-whitespace'),
{ src: path.join(NUXT_SHELL, 'plugins/extend-router') },
{ src: path.join(NUXT_SHELL, 'plugins/console'), ssr: false },
{ src: path.join(NUXT_SHELL, 'plugins/int-number'), ssr: false },
{ src: path.join(NUXT_SHELL, 'plugins/nuxt-client-init'), ssr: false },
path.join(NUXT_SHELL, 'plugins/replaceall'),
path.join(NUXT_SHELL, 'plugins/back-button'),
{ src: path.join(NUXT_SHELL, 'plugins/plugin'), ssr: false }, // Load dyanmic plugins
{ src: path.join(NUXT_SHELL, 'plugins/codemirror-loader'), ssr: false },
{ src: path.join(NUXT_SHELL, 'plugins/formatters'), ssr: false }, // Populate formatters cache for sorted table
{ src: path.join(NUXT_SHELL, 'plugins/version'), ssr: false }, // Makes a fetch to the backend to get version metadata
{ src: path.join(NUXT_SHELL, 'plugins/steve-create-worker'), ssr: false }, // Add steve web worker creator to the store, to break the import chain
],
// Proxy: https://github.com/nuxt-community/proxy-module#options
proxy: {
...appConfig.proxies,
'/k8s': proxyWsOpts(api), // Straight to a remote cluster (/k8s/clusters/<id>/)
'/pp': proxyWsOpts(api), // For (epinio) standalone API
'/api': proxyWsOpts(api), // Management k8s API
'/apis': proxyWsOpts(api), // Management k8s API
'/v1': proxyWsOpts(api), // Management Steve API
'/v3': proxyWsOpts(api), // Rancher API
'/v3-public': proxyOpts(api), // Rancher Unauthed API
'/api-ui': proxyOpts(api), // Browser API UI
'/meta': proxyMetaOpts(api), // Browser API UI
'/v1-*': proxyOpts(api), // SAML, KDM, etc
'/rancherversion': proxyPrimeOpts(api), // Rancher version endpoint
// These are for Ember embedding
'/c/*/edit': proxyOpts('https://127.0.0.1:8000'), // Can't proxy all of /c because that's used by Vue too
'/k/': proxyOpts('https://127.0.0.1:8000'),
'/g/': proxyOpts('https://127.0.0.1:8000'),
'/n/': proxyOpts('https://127.0.0.1:8000'),
'/p/': proxyOpts('https://127.0.0.1:8000'),
'/assets': proxyOpts('https://127.0.0.1:8000'),
'/translations': proxyOpts('https://127.0.0.1:8000'),
'/engines-dist': proxyOpts('https://127.0.0.1:8000'),
},
// Nuxt server
server: {
https: (devPorts ? {
key: fs.readFileSync(path.resolve(dir, SHELL, 'server/server.key')),
cert: fs.readFileSync(path.resolve(dir, SHELL, 'server/server.crt'))
} : null),
port: (devPorts ? 8005 : 80),
host: '0.0.0.0',
},
// Server middleware
serverMiddleware,
// Eslint module options
eslint: {
cache: path.join(dir, 'node_modules/.cache/eslint'),
exclude: [
'.nuxt'
]
},
// Typescript eslint
typescript,
ssr: false,
};
return config;
}
// ===============================================================================================
// Functions for the request proxying used in dev
// ===============================================================================================
export function proxyMetaOpts(target) {
return {
target,
followRedirects: true,
secure: !dev,
onProxyReq,
onProxyReqWs,
onError,
onProxyRes,
};
}
export function proxyOpts(target) {
return {
target,
secure: !devPorts,
onProxyReq,
onProxyReqWs,
onError,
onProxyRes,
};
}
// Intercept the /rancherversion API call wnad modify the 'RancherPrime' value
// if configured to do so by the environment variable PRIME
export function proxyPrimeOpts(target) {
const opts = proxyOpts(target);
// Don't intercept if the PRIME environment variable is not set
if (!prime?.length) {
return opts;
}
opts.onProxyRes = (proxyRes, req, res) => {
const _end = res.end;
let body = '';
proxyRes.on( 'data', (data) => {
data = data.toString('utf-8');
body += data;
});
res.write = () => {};
res.end = () => {
let output = body;
try {
const out = JSON.parse(body);
out.RancherPrime = prime;
output = JSON.stringify(out);
} catch (err) {}
res.setHeader('content-length', output.length );
res.setHeader('content-type', 'application/json' );
res.setHeader('transfer-encoding', '');
res.setHeader('cache-control', 'no-cache');
res.writeHead(proxyRes.statusCode);
_end.apply(res, [output]);
};
};
return opts;
}
export function onProxyRes(proxyRes, req, res) {
if (devPorts) {
proxyRes.headers['X-Frame-Options'] = 'ALLOWALL';
}
}
export function proxyWsOpts(target) {
return {
...proxyOpts(target),
ws: true,
changeOrigin: true,
};
}
export function onProxyReq(proxyReq, req) {
if (!(proxyReq._currentRequest && proxyReq._currentRequest._headerSent)) {
proxyReq.setHeader('x-api-host', req.headers['host']);
proxyReq.setHeader('x-forwarded-proto', 'https');
// console.log(proxyReq.getHeaders());
}
}
export function onProxyReqWs(proxyReq, req, socket, options, head) {
req.headers.origin = options.target.href;
proxyReq.setHeader('origin', options.target.href);
proxyReq.setHeader('x-api-host', req.headers['host']);
proxyReq.setHeader('x-forwarded-proto', 'https');
// console.log(proxyReq.getHeaders());
socket.on('error', (err) => {
console.error('Proxy WS Error:', err); // eslint-disable-line no-console
});
}
export function onError(err, req, res) {
res.statusCode = 598;
console.error('Proxy Error:', err); // eslint-disable-line no-console
res.write(JSON.stringify(err));
}

210
shell/nuxt/App.js Normal file
View File

@ -0,0 +1,210 @@
import Vue from 'vue'
import { decode, parsePath, withoutBase, withoutTrailingSlash, normalizeURL } from 'ufo'
import { getMatchedComponentsInstances, getChildrenComponentInstancesUsingFetch, promisify, globalHandleError, urlJoin, sanitizeComponent } from './utils'
import NuxtError from '../layouts/error.vue'
import NuxtLoading from '../components/nav/GlobalLoading.vue'
import NuxtBuildIndicator from './components/nuxt-build-indicator'
import '../assets/styles/app.scss'
import _77180f1e from '../layouts/blank.vue'
import _6f6c098b from '../layouts/default.vue'
import _2d2495d5 from '../layouts/home.vue'
import _77dd5794 from '../layouts/plain.vue'
import _44002f80 from '../layouts/unauthenticated.vue'
const layouts = { "_blank": sanitizeComponent(_77180f1e),"_default": sanitizeComponent(_6f6c098b),"_home": sanitizeComponent(_2d2495d5),"_plain": sanitizeComponent(_77dd5794),"_unauthenticated": sanitizeComponent(_44002f80) }
export default {
render (h, props) {
const loadingEl = h('NuxtLoading', { ref: 'loading' })
const layoutEl = h(this.layout || 'nuxt')
const templateEl = h('div', {
domProps: {
id: '__layout'
},
key: this.layoutName
}, [layoutEl])
const transitionEl = h('transition', {
props: {
name: 'layout',
mode: 'out-in'
},
on: {
beforeEnter (el) {
// Ensure to trigger scroll event after calling scrollBehavior
window.$nuxt.$nextTick(() => {
window.$nuxt.$emit('triggerScroll')
})
}
}
}, [templateEl])
return h('div', {
domProps: {
id: '__nuxt'
}
}, [
loadingEl,
//h(NuxtBuildIndicator), // The build indicator doesn't work as is right now and emits an error in the console so I'm leaving it out for now
transitionEl
])
},
data: () => ({
isOnline: true,
layout: null,
layoutName: '',
nbFetching: 0
}),
beforeCreate () {
Vue.util.defineReactive(this, 'nuxt', this.$options.nuxt)
},
created () {
// Add this.$nuxt in child instances
this.$root.$options.$nuxt = this
if (process.client) {
// add to window so we can listen when ready
window.$nuxt = this
this.refreshOnlineStatus()
// Setup the listeners
window.addEventListener('online', this.refreshOnlineStatus)
window.addEventListener('offline', this.refreshOnlineStatus)
}
// Add $nuxt.error()
this.error = this.nuxt.error
// Add $nuxt.context
this.context = this.$options.context
},
async mounted () {
this.$loading = this.$refs.loading
},
watch: {
'nuxt.err': 'errorChanged'
},
computed: {
isOffline () {
return !this.isOnline
},
isFetching () {
return this.nbFetching > 0
},
},
methods: {
refreshOnlineStatus () {
if (process.client) {
if (typeof window.navigator.onLine === 'undefined') {
// If the browser doesn't support connection status reports
// assume that we are online because most apps' only react
// when they now that the connection has been interrupted
this.isOnline = true
} else {
this.isOnline = window.navigator.onLine
}
}
},
async refresh () {
const pages = getMatchedComponentsInstances(this.$route)
if (!pages.length) {
return
}
this.$loading.start()
const promises = pages.map((page) => {
const p = []
// Old fetch
if (page.$options.fetch && page.$options.fetch.length) {
p.push(promisify(page.$options.fetch, this.context))
}
if (page.$fetch) {
p.push(page.$fetch())
} else {
// Get all component instance to call $fetch
for (const component of getChildrenComponentInstancesUsingFetch(page.$vnode.componentInstance)) {
p.push(component.$fetch())
}
}
if (page.$options.asyncData) {
p.push(
promisify(page.$options.asyncData, this.context)
.then((newData) => {
for (const key in newData) {
Vue.set(page.$data, key, newData[key])
}
})
)
}
return Promise.all(p)
})
try {
await Promise.all(promises)
} catch (error) {
this.$loading.fail(error)
globalHandleError(error)
this.error(error)
}
this.$loading.finish()
},
errorChanged () {
if (this.nuxt.err) {
if (this.$loading) {
if (this.$loading.fail) {
this.$loading.fail(this.nuxt.err)
}
if (this.$loading.finish) {
this.$loading.finish()
}
}
let errorLayout = (NuxtError.options || NuxtError).layout;
if (typeof errorLayout === 'function') {
errorLayout = errorLayout(this.context)
}
this.setLayout(errorLayout)
}
},
setLayout (layout) {
if(layout && typeof layout !== 'string') {
throw new Error('[nuxt] Avoid using non-string value as layout property.')
}
if (!layout || !layouts['_' + layout]) {
layout = 'default'
}
this.layoutName = layout
this.layout = layouts['_' + layout]
return this.layout
},
loadLayout (layout) {
if (!layout || !layouts['_' + layout]) {
layout = 'default'
}
return Promise.resolve(layouts['_' + layout])
},
},
components: {
NuxtLoading
}
}

186
shell/nuxt/axios.js Normal file
View File

@ -0,0 +1,186 @@
import Axios from 'axios'
import defu from 'defu'
import axiosRetry from 'axios-retry'
// Axios.prototype cannot be modified
const axiosExtra = {
setBaseURL (baseURL) {
this.defaults.baseURL = baseURL
},
setHeader (name, value, scopes = 'common') {
for (let scope of Array.isArray(scopes) ? scopes : [ scopes ]) {
if (!value) {
delete this.defaults.headers[scope][name];
return
}
this.defaults.headers[scope][name] = value
}
},
setToken (token, type, scopes = 'common') {
const value = !token ? null : (type ? type + ' ' : '') + token
this.setHeader('Authorization', value, scopes)
},
onRequest(fn) {
this.interceptors.request.use(config => fn(config) || config)
},
onResponse(fn) {
this.interceptors.response.use(response => fn(response) || response)
},
onRequestError(fn) {
this.interceptors.request.use(undefined, error => fn(error) || Promise.reject(error))
},
onResponseError(fn) {
this.interceptors.response.use(undefined, error => fn(error) || Promise.reject(error))
},
onError(fn) {
this.onRequestError(fn)
this.onResponseError(fn)
},
create(options) {
return createAxiosInstance(defu(options, this.defaults))
}
}
// Request helpers ($get, $post, ...)
for (let method of ['request', 'delete', 'get', 'head', 'options', 'post', 'put', 'patch']) {
axiosExtra['$' + method] = function () { return this[method].apply(this, arguments).then(res => res && res.data) }
}
const extendAxiosInstance = axios => {
for (let key in axiosExtra) {
axios[key] = axiosExtra[key].bind(axios)
}
}
const createAxiosInstance = axiosOptions => {
// Create new axios instance
const axios = Axios.create(axiosOptions)
axios.CancelToken = Axios.CancelToken
axios.isCancel = Axios.isCancel
// Extend axios proto
extendAxiosInstance(axios)
// Setup interceptors
setupProgress(axios)
axiosRetry(axios, {"retries":0})
return axios
}
const setupProgress = (axios) => {
if (process.server) {
return
}
// A noop loading inteterface for when $nuxt is not yet ready
const noopLoading = {
finish: () => { },
start: () => { },
fail: () => { },
set: () => { }
}
const $loading = () => {
const $nuxt = typeof window !== 'undefined' && window['$nuxt']
return ($nuxt && $nuxt.$loading && $nuxt.$loading.set) ? $nuxt.$loading : noopLoading
}
let currentRequests = 0
axios.onRequest(config => {
if (config && config.progress === false) {
return
}
currentRequests++
})
axios.onResponse(response => {
if (response && response.config && response.config.progress === false) {
return
}
currentRequests--
if (currentRequests <= 0) {
currentRequests = 0
$loading().finish()
}
})
axios.onError(error => {
if (error && error.config && error.config.progress === false) {
return
}
currentRequests--
if (Axios.isCancel(error)) {
return
}
$loading().fail()
$loading().finish()
})
const onProgress = e => {
if (!currentRequests) {
return
}
const progress = ((e.loaded * 100) / (e.total * currentRequests))
$loading().set(Math.min(100, progress))
}
axios.defaults.onUploadProgress = onProgress
axios.defaults.onDownloadProgress = onProgress
}
export default (ctx, inject) => {
// runtimeConfig
const runtimeConfig = ctx.$config && ctx.$config.axios || {}
// baseURL
const baseURL = process.browser
? (runtimeConfig.browserBaseURL || runtimeConfig.baseURL || '/')
: (runtimeConfig.baseURL || process.env._AXIOS_BASE_URL_ || 'https://localhost:8005/')
// Create fresh objects for all default header scopes
// Axios creates only one which is shared across SSR requests!
// https://github.com/mzabriskie/axios/blob/master/lib/defaults.js
const headers = {
"common": {
"Accept": "application/json, text/plain, */*"
},
"delete": {},
"get": {},
"head": {},
"post": {},
"put": {},
"patch": {}
}
const axiosOptions = {
baseURL,
headers
}
// Proxy SSR request headers headers
if (process.server && ctx.req && ctx.req.headers) {
const reqHeaders = { ...ctx.req.headers }
for (let h of ["accept","host","cf-ray","cf-connecting-ip","content-length","content-md5","content-type"]) {
delete reqHeaders[h]
}
axiosOptions.headers.common = { ...reqHeaders, ...axiosOptions.headers.common }
}
if (process.server) {
// Don't accept brotli encoding because Node can't parse it
axiosOptions.headers.common['accept-encoding'] = 'gzip, deflate'
}
const axios = createAxiosInstance(axiosOptions)
// Inject axios to the context as $axios
ctx.$axios = axios
inject('axios', axios)
}

817
shell/nuxt/client.js Normal file
View File

@ -0,0 +1,817 @@
import Vue from 'vue'
import fetch from 'unfetch'
import middleware from './middleware.js'
import {
applyAsyncData,
promisify,
middlewareSeries,
sanitizeComponent,
resolveRouteComponents,
getMatchedComponents,
getMatchedComponentsInstances,
flatMapComponents,
setContext,
getLocation,
compile,
getQueryDiff,
globalHandleError,
isSamePath,
urlJoin
} from './utils.js'
import { createApp, NuxtError } from './index.js'
import fetchMixin from './mixins/fetch.client'
import NuxtLink from './components/nuxt-link.client.js' // should be included after ./index.js
// Fetch mixin
if (!Vue.__nuxt__fetch__mixin__) {
Vue.mixin(fetchMixin)
Vue.__nuxt__fetch__mixin__ = true
}
// Component: <NuxtLink>
Vue.component(NuxtLink.name, NuxtLink)
Vue.component('NLink', NuxtLink)
if (!global.fetch) { global.fetch = fetch }
// Global shared references
let _lastPaths = []
let app
let router
let store
// Try to rehydrate SSR data from window
const NUXT = window.__NUXT__ || {}
const $config = nuxt.publicRuntimeConfig || {}
if ($config._app) {
__webpack_public_path__ = urlJoin($config._app.cdnURL, $config._app.assetsPath)
}
Object.assign(Vue.config, {"silent":false,"performance":true})
const logs = NUXT.logs || []
if (logs.length > 0) {
const ssrLogStyle = 'background: #2E495E;border-radius: 0.5em;color: white;font-weight: bold;padding: 2px 0.5em;'
console.group && console.group ('%cNuxt SSR', ssrLogStyle)
logs.forEach(logObj => (console[logObj.type] || console.log)(...logObj.args))
delete NUXT.logs
console.groupEnd && console.groupEnd()
}
// Setup global Vue error handler
if (!Vue.config.$nuxt) {
const defaultErrorHandler = Vue.config.errorHandler
Vue.config.errorHandler = async (err, vm, info, ...rest) => {
// Call other handler if exist
let handled = null
if (typeof defaultErrorHandler === 'function') {
handled = defaultErrorHandler(err, vm, info, ...rest)
}
if (handled === true) {
return handled
}
if (vm && vm.$root) {
const nuxtApp = Object.keys(Vue.config.$nuxt)
.find(nuxtInstance => vm.$root[nuxtInstance])
// Show Nuxt Error Page
if (nuxtApp && vm.$root[nuxtApp].error && info !== 'render function') {
const currentApp = vm.$root[nuxtApp]
// Load error layout
let layout = (NuxtError.options || NuxtError).layout
if (typeof layout === 'function') {
layout = layout(currentApp.context)
}
if (layout) {
await currentApp.loadLayout(layout).catch(() => {})
}
currentApp.setLayout(layout)
currentApp.error(err)
}
}
if (typeof defaultErrorHandler === 'function') {
return handled
}
// Log to console
if (process.env.NODE_ENV !== 'production') {
console.error(err)
} else {
console.error(err.message || err)
}
}
Vue.config.$nuxt = {}
}
Vue.config.$nuxt.$nuxt = true
const errorHandler = Vue.config.errorHandler || console.error
// Create and mount App
createApp(null, nuxt.publicRuntimeConfig).then(mountApp).catch(errorHandler)
function componentOption (component, key, ...args) {
if (!component || !component.options || !component.options[key]) {
return {}
}
const option = component.options[key]
if (typeof option === 'function') {
return option(...args)
}
return option
}
function mapTransitions (toComponents, to, from) {
const componentTransitions = (component) => {
const transition = componentOption(component, 'transition', to, from) || {}
return (typeof transition === 'string' ? { name: transition } : transition)
}
const fromComponents = from ? getMatchedComponents(from) : []
const maxDepth = Math.max(toComponents.length, fromComponents.length)
const mergedTransitions = []
for (let i=0; i<maxDepth; i++) {
// Clone original objects to prevent overrides
const toTransitions = Object.assign({}, componentTransitions(toComponents[i]))
const transitions = Object.assign({}, componentTransitions(fromComponents[i]))
// Combine transitions & prefer `leave` properties of "from" route
Object.keys(toTransitions)
.filter(key => typeof toTransitions[key] !== 'undefined' && !key.toLowerCase().includes('leave'))
.forEach((key) => { transitions[key] = toTransitions[key] })
mergedTransitions.push(transitions)
}
return mergedTransitions
}
async function loadAsyncComponents (to, from, next) {
// Check if route changed (this._routeChanged), only if the page is not an error (for validate())
this._routeChanged = Boolean(app.nuxt.err) || from.name !== to.name
this._paramChanged = !this._routeChanged && from.path !== to.path
this._queryChanged = !this._paramChanged && from.fullPath !== to.fullPath
this._diffQuery = (this._queryChanged ? getQueryDiff(to.query, from.query) : [])
if ((this._routeChanged || this._paramChanged) && this.$loading.start && !this.$loading.manual) {
this.$loading.start()
}
try {
if (this._queryChanged) {
const Components = await resolveRouteComponents(
to,
(Component, instance) => ({ Component, instance })
)
// Add a marker on each component that it needs to refresh or not
const startLoader = Components.some(({ Component, instance }) => {
const watchQuery = Component.options.watchQuery
if (watchQuery === true) {
return true
}
if (Array.isArray(watchQuery)) {
return watchQuery.some(key => this._diffQuery[key])
}
if (typeof watchQuery === 'function') {
return watchQuery.apply(instance, [to.query, from.query])
}
return false
})
if (startLoader && this.$loading.start && !this.$loading.manual) {
this.$loading.start()
}
}
// Call next()
next()
} catch (error) {
const err = error || {}
const statusCode = err.statusCode || err.status || (err.response && err.response.status) || 500
const message = err.message || ''
// Handle chunk loading errors
// This may be due to a new deployment or a network problem
if (/^Loading( CSS)? chunk (\d)+ failed\./.test(message)) {
window.location.reload(true /* skip cache */)
return // prevent error page blinking for user
}
this.error({ statusCode, message })
this.$nuxt.$emit('routeChanged', to, from, err)
next()
}
}
function applySSRData (Component, ssrData) {
if (NUXT.serverRendered && ssrData) {
applyAsyncData(Component, ssrData)
}
Component._Ctor = Component
return Component
}
// Get matched components
function resolveComponents (route) {
return flatMapComponents(route, async (Component, _, match, key, index) => {
// If component is not resolved yet, resolve it
if (typeof Component === 'function' && !Component.options) {
Component = await Component()
}
// Sanitize it and save it
const _Component = applySSRData(sanitizeComponent(Component), NUXT.data ? NUXT.data[index] : null)
match.components[key] = _Component
return _Component
})
}
function callMiddleware (Components, context, layout) {
let midd = ["i18n"]
let unknownMiddleware = false
// If layout is undefined, only call global middleware
if (typeof layout !== 'undefined') {
midd = [] // Exclude global middleware if layout defined (already called before)
layout = sanitizeComponent(layout)
if (layout.options.middleware) {
midd = midd.concat(layout.options.middleware)
}
Components.forEach((Component) => {
if (Component.options.middleware) {
midd = midd.concat(Component.options.middleware)
}
})
}
midd = midd.map((name) => {
if (typeof name === 'function') {
return name
}
if (typeof middleware[name] !== 'function') {
unknownMiddleware = true
this.error({ statusCode: 500, message: 'Unknown middleware ' + name })
}
return middleware[name]
})
if (unknownMiddleware) {
return
}
return middlewareSeries(midd, context)
}
async function render (to, from, next) {
if (this._routeChanged === false && this._paramChanged === false && this._queryChanged === false) {
return next()
}
// Handle first render on SPA mode
let spaFallback = false
if (to === from) {
_lastPaths = []
spaFallback = true
} else {
const fromMatches = []
_lastPaths = getMatchedComponents(from, fromMatches).map((Component, i) => {
return compile(from.matched[fromMatches[i]].path)(from.params)
})
}
// nextCalled is true when redirected
let nextCalled = false
const _next = (path) => {
if (from.path === path.path && this.$loading.finish) {
this.$loading.finish()
}
if (from.path !== path.path && this.$loading.pause) {
this.$loading.pause()
}
if (nextCalled) {
return
}
nextCalled = true
next(path)
}
// Update context
await setContext(app, {
route: to,
from,
next: _next.bind(this)
})
this._dateLastError = app.nuxt.dateErr
this._hadError = Boolean(app.nuxt.err)
// Get route's matched components
const matches = []
const Components = getMatchedComponents(to, matches)
// If no Components matched, generate 404
if (!Components.length) {
// Default layout
await callMiddleware.call(this, Components, app.context)
if (nextCalled) {
return
}
// Load layout for error page
const errorLayout = (NuxtError.options || NuxtError).layout
const layout = await this.loadLayout(
typeof errorLayout === 'function'
? errorLayout.call(NuxtError, app.context)
: errorLayout
)
await callMiddleware.call(this, Components, app.context, layout)
if (nextCalled) {
return
}
// Show error page
app.context.error({ statusCode: 404, message: 'This page could not be found' })
return next()
}
// Update ._data and other properties if hot reloaded
Components.forEach((Component) => {
if (Component._Ctor && Component._Ctor.options) {
Component.options.asyncData = Component._Ctor.options.asyncData
Component.options.fetch = Component._Ctor.options.fetch
}
})
// Apply transitions
this.setTransitions(mapTransitions(Components, to, from))
try {
// Call middleware
await callMiddleware.call(this, Components, app.context)
if (nextCalled) {
return
}
if (app.context._errored) {
return next()
}
// Set layout
let layout = Components[0].options.layout
if (typeof layout === 'function') {
layout = layout(app.context)
}
layout = await this.loadLayout(layout)
// Call middleware for layout
await callMiddleware.call(this, Components, app.context, layout)
if (nextCalled) {
return
}
if (app.context._errored) {
return next()
}
// Call .validate()
let isValid = true
try {
for (const Component of Components) {
if (typeof Component.options.validate !== 'function') {
continue
}
isValid = await Component.options.validate(app.context)
if (!isValid) {
break
}
}
} catch (validationError) {
// ...If .validate() threw an error
this.error({
statusCode: validationError.statusCode || '500',
message: validationError.message
})
return next()
}
// ...If .validate() returned false
if (!isValid) {
this.error({ statusCode: 404, message: 'This page could not be found' })
return next()
}
let instances
// Call asyncData & fetch hooks on components matched by the route.
await Promise.all(Components.map(async (Component, i) => {
// Check if only children route changed
Component._path = compile(to.matched[matches[i]].path)(to.params)
Component._dataRefresh = false
const childPathChanged = Component._path !== _lastPaths[i]
// Refresh component (call asyncData & fetch) when:
// Route path changed part includes current component
// Or route param changed part includes current component and watchParam is not `false`
// Or route query is changed and watchQuery returns `true`
if (this._routeChanged && childPathChanged) {
Component._dataRefresh = true
} else if (this._paramChanged && childPathChanged) {
const watchParam = Component.options.watchParam
Component._dataRefresh = watchParam !== false
} else if (this._queryChanged) {
const watchQuery = Component.options.watchQuery
if (watchQuery === true) {
Component._dataRefresh = true
} else if (Array.isArray(watchQuery)) {
Component._dataRefresh = watchQuery.some(key => this._diffQuery[key])
} else if (typeof watchQuery === 'function') {
if (!instances) {
instances = getMatchedComponentsInstances(to)
}
Component._dataRefresh = watchQuery.apply(instances[i], [to.query, from.query])
}
}
if (!this._hadError && this._isMounted && !Component._dataRefresh) {
return
}
const promises = []
const hasAsyncData = (
Component.options.asyncData &&
typeof Component.options.asyncData === 'function'
)
const hasFetch = Boolean(Component.options.fetch) && Component.options.fetch.length
const loadingIncrease = (hasAsyncData && hasFetch) ? 30 : 45
// Call asyncData(context)
if (hasAsyncData) {
const promise = promisify(Component.options.asyncData, app.context)
promise.then((asyncDataResult) => {
applyAsyncData(Component, asyncDataResult)
if (this.$loading.increase) {
this.$loading.increase(loadingIncrease)
}
})
promises.push(promise)
}
// Check disabled page loading
this.$loading.manual = Component.options.loading === false
// Call fetch(context)
if (hasFetch) {
let p = Component.options.fetch(app.context)
if (!p || (!(p instanceof Promise) && (typeof p.then !== 'function'))) {
p = Promise.resolve(p)
}
p.then((fetchResult) => {
if (this.$loading.increase) {
this.$loading.increase(loadingIncrease)
}
})
promises.push(p)
}
return Promise.all(promises)
}))
// If not redirected
if (!nextCalled) {
if (this.$loading.finish && !this.$loading.manual) {
this.$loading.finish()
}
next()
}
} catch (err) {
const error = err || {}
if (error.message === 'ERR_REDIRECT') {
return this.$nuxt.$emit('routeChanged', to, from, error)
}
_lastPaths = []
globalHandleError(error)
// Load error layout
let layout = (NuxtError.options || NuxtError).layout
if (typeof layout === 'function') {
layout = layout(app.context)
}
await this.loadLayout(layout)
this.error(error)
this.$nuxt.$emit('routeChanged', to, from, error)
next()
}
}
// Fix components format in matched, it's due to code-splitting of vue-router
function normalizeComponents (to, ___) {
flatMapComponents(to, (Component, _, match, key) => {
if (typeof Component === 'object' && !Component.options) {
// Updated via vue-router resolveAsyncComponents()
Component = Vue.extend(Component)
Component._Ctor = Component
match.components[key] = Component
}
return Component
})
}
function setLayoutForNextPage (to) {
// Set layout
let hasError = Boolean(this.$options.nuxt.err)
if (this._hadError && this._dateLastError === this.$options.nuxt.dateErr) {
hasError = false
}
let layout = hasError
? (NuxtError.options || NuxtError).layout
: to.matched[0].components.default.options.layout
if (typeof layout === 'function') {
layout = layout(app.context)
}
this.setLayout(layout)
}
function checkForErrors (app) {
// Hide error component if no error
if (app._hadError && app._dateLastError === app.$options.nuxt.dateErr) {
app.error()
}
}
// When navigating on a different route but the same component is used, Vue.js
// Will not update the instance data, so we have to update $data ourselves
function fixPrepatch (to, ___) {
if (this._routeChanged === false && this._paramChanged === false && this._queryChanged === false) {
return
}
const instances = getMatchedComponentsInstances(to)
const Components = getMatchedComponents(to)
let triggerScroll = false
Vue.nextTick(() => {
instances.forEach((instance, i) => {
if (!instance || instance._isDestroyed) {
return
}
if (
instance.constructor._dataRefresh &&
Components[i] === instance.constructor &&
instance.$vnode.data.keepAlive !== true &&
typeof instance.constructor.options.data === 'function'
) {
const newData = instance.constructor.options.data.call(instance)
for (const key in newData) {
Vue.set(instance.$data, key, newData[key])
}
triggerScroll = true
}
})
if (triggerScroll) {
// Ensure to trigger scroll event after calling scrollBehavior
window.$nuxt.$nextTick(() => {
window.$nuxt.$emit('triggerScroll')
})
}
checkForErrors(this)
// Hot reloading
setTimeout(() => hotReloadAPI(this), 100)
})
}
function nuxtReady (_app) {
window.onNuxtReadyCbs.forEach((cb) => {
if (typeof cb === 'function') {
cb(_app)
}
})
// Special JSDOM
if (typeof window._onNuxtLoaded === 'function') {
window._onNuxtLoaded(_app)
}
// Add router hooks
router.afterEach((to, from) => {
// Wait for fixPrepatch + $data updates
Vue.nextTick(() => _app.$nuxt.$emit('routeChanged', to, from))
})
}
const noopData = () => { return {} }
const noopFetch = () => {}
// Special hot reload with asyncData(context)
function getNuxtChildComponents ($parent, $components = []) {
$parent.$children.forEach(($child) => {
if ($child.$vnode && $child.$vnode.data.nuxtChild && !$components.find(c =>(c.$options.__file === $child.$options.__file))) {
$components.push($child)
}
if ($child.$children && $child.$children.length) {
getNuxtChildComponents($child, $components)
}
})
return $components
}
function hotReloadAPI(_app) {
if (!module.hot) return
let $components = getNuxtChildComponents(_app.$nuxt, [])
$components.forEach(addHotReload.bind(_app))
}
function addHotReload ($component, depth) {
if ($component.$vnode.data._hasHotReload) return
$component.$vnode.data._hasHotReload = true
var _forceUpdate = $component.$forceUpdate.bind($component.$parent)
$component.$vnode.context.$forceUpdate = async () => {
let Components = getMatchedComponents(router.currentRoute)
let Component = Components[depth]
if (!Component) {
return _forceUpdate()
}
if (typeof Component === 'object' && !Component.options) {
// Updated via vue-router resolveAsyncComponents()
Component = Vue.extend(Component)
Component._Ctor = Component
}
this.error()
let promises = []
const next = function (path) {
this.$loading.finish && this.$loading.finish()
router.push(path)
}
await setContext(app, {
route: router.currentRoute,
isHMR: true,
next: next.bind(this)
})
const context = app.context
if (this.$loading.start && !this.$loading.manual) {
this.$loading.start()
}
callMiddleware.call(this, Components, context)
.then(() => {
// If layout changed
if (depth !== 0) {
return
}
let layout = Component.options.layout || 'default'
if (typeof layout === 'function') {
layout = layout(context)
}
if (this.layoutName === layout) {
return
}
let promise = this.loadLayout(layout)
promise.then(() => {
this.setLayout(layout)
Vue.nextTick(() => hotReloadAPI(this))
})
return promise
})
.then(() => {
return callMiddleware.call(this, Components, context, this.layout)
})
.then(() => {
// Call asyncData(context)
let pAsyncData = promisify(Component.options.asyncData || noopData, context)
pAsyncData.then((asyncDataResult) => {
applyAsyncData(Component, asyncDataResult)
this.$loading.increase && this.$loading.increase(30)
})
promises.push(pAsyncData)
// Call fetch()
Component.options.fetch = Component.options.fetch || noopFetch
let pFetch = Component.options.fetch.length && Component.options.fetch(context)
if (!pFetch || (!(pFetch instanceof Promise) && (typeof pFetch.then !== 'function'))) { pFetch = Promise.resolve(pFetch) }
pFetch.then(() => this.$loading.increase && this.$loading.increase(30))
promises.push(pFetch)
return Promise.all(promises)
})
.then(() => {
this.$loading.finish && this.$loading.finish()
_forceUpdate()
setTimeout(() => hotReloadAPI(this), 100)
})
}
}
async function mountApp (__app) {
// Set global variables
app = __app.app
router = __app.router
store = __app.store
// Create Vue instance
const _app = new Vue(app)
// Mounts Vue app to DOM element
const mount = () => {
_app.$mount('#app')
// Add afterEach router hooks
router.afterEach(normalizeComponents)
router.afterEach(setLayoutForNextPage.bind(_app))
router.afterEach(fixPrepatch.bind(_app))
// Listen for first Vue update
Vue.nextTick(() => {
// Call window.{{globals.readyCallback}} callbacks
nuxtReady(_app)
// Enable hot reloading
hotReloadAPI(_app)
})
}
// Resolve route components
const Components = await Promise.all(resolveComponents(app.context.route))
// Enable transitions
_app.setTransitions = _app.$options.nuxt.setTransitions.bind(_app)
if (Components.length) {
_app.setTransitions(mapTransitions(Components, router.currentRoute))
_lastPaths = router.currentRoute.matched.map(route => compile(route.path)(router.currentRoute.params))
}
// Initialize error handler
_app.$loading = {} // To avoid error while _app.$nuxt does not exist
if (NUXT.error) {
_app.error(NUXT.error)
}
// Add beforeEach router hooks
router.beforeEach(loadAsyncComponents.bind(_app))
router.beforeEach(render.bind(_app))
// Fix in static: remove trailing slash to force hydration
// Full static, if server-rendered: hydrate, to allow custom redirect to generated page
// Fix in static: remove trailing slash to force hydration
if (NUXT.serverRendered && isSamePath(NUXT.routePath, _app.context.route.path)) {
return mount()
}
// First render on client-side
const clientFirstMount = () => {
normalizeComponents(router.currentRoute, router.currentRoute)
setLayoutForNextPage.call(_app, router.currentRoute)
checkForErrors(_app)
// Don't call fixPrepatch.call(_app, router.currentRoute, router.currentRoute) since it's first render
mount()
}
// fix: force next tick to avoid having same timestamp when an error happen on spa fallback
await new Promise(resolve => setTimeout(resolve, 0))
render.call(_app, router.currentRoute, router.currentRoute, (path) => {
// If not redirected
if (!path) {
clientFirstMount()
return
}
// Add a one-time afterEach hook to
// mount the app wait for redirect and route gets resolved
const unregisterHook = router.afterEach((to, from) => {
unregisterHook()
clientFirstMount()
})
// Push the path and let route to be resolved
router.push(path, undefined, (err) => {
if (err) {
errorHandler(err)
}
})
})
}

View File

@ -0,0 +1,143 @@
<template>
<transition appear>
<div v-if="building" class="nuxt__build_indicator" :style="indicatorStyle">
<svg viewBox="0 0 96 72" version="1" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd">
<path d="M6 66h23l1-3 21-37L40 6 6 66zM79 66h11L62 17l-5 9 22 37v3zM54 31L35 66h38z" />
<path d="M29 69v-1-2H6L40 6l11 20 3-6L44 3s-2-3-4-3-3 1-5 3L1 63c0 1-2 3 0 6 0 1 2 2 5 2h28c-3 0-4-1-5-2z" fill="#00C58E" />
<path d="M95 63L67 14c0-1-2-3-5-3-1 0-3 0-4 3l-4 6 3 6 5-9 28 49H79a5 5 0 0 1 0 3c-2 2-5 2-5 2h16c1 0 4 0 5-2 1-1 2-3 0-6z" fill="#00C58E" />
<path d="M79 69v-1-2-3L57 26l-3-6-3 6-21 37-1 3a5 5 0 0 0 0 3c1 1 2 2 5 2h40s3 0 5-2zM54 31l19 35H35l19-35z" fill="#FFF" fill-rule="nonzero" />
</g>
</svg>
{{ animatedProgress }}%
</div>
</transition>
</template>
<script>
export default {
name: 'NuxtBuildIndicator',
data () {
return {
building: false,
progress: 0,
animatedProgress: 0,
reconnectAttempts: 0
}
},
computed: {
options: () => ({"position":"bottom-right","backgroundColor":"#2E495E","color":"#00C48D"}),
indicatorStyle () {
const [d1, d2] = this.options.position.split('-')
return {
[d1]: '20px',
[d2]: '20px',
'background-color': this.options.backgroundColor,
color: this.options.color
}
}
},
watch: {
progress (val, oldVal) {
// Average progress may decrease but ignore it!
if (val < oldVal) {
return
}
// Cancel old animation
clearInterval(this._progressAnimation)
// Jump to edge immediately
if (val < 10 || val > 90) {
this.animatedProgress = val
return
}
// Animate to value
this._progressAnimation = setInterval(() => {
const diff = this.progress - this.animatedProgress
if (diff > 0) {
this.animatedProgress++
} else {
clearInterval(this._progressAnimation)
}
}, 50)
}
},
mounted () {
if (EventSource === undefined) {
return // Unsupported
}
this.sseConnect()
},
beforeDestroy () {
this.sseClose()
clearInterval(this._progressAnimation)
},
methods: {
sseConnect () {
if (this._connecting) {
return
}
this._connecting = true
this.sse = new EventSource('/_loading/sse')
this.sse.addEventListener('message', event => this.onSseMessage(event))
},
onSseMessage (message) {
const data = JSON.parse(message.data)
if (!data.states) {
return
}
this.progress = Math.round(data.states.reduce((p, s) => p + s.progress, 0) / data.states.length)
if (!data.allDone) {
this.building = true
} else {
this.$nextTick(() => {
this.building = false
this.animatedProgress = 0
this.progress = 0
clearInterval(this._progressAnimation)
})
}
},
sseClose () {
if (this.sse) {
this.sse.close()
delete this.sse
}
}
}
}
</script>
<style scoped>
.nuxt__build_indicator {
box-sizing: border-box;
position: fixed;
font-family: monospace;
padding: 5px 10px;
border-radius: 5px;
box-shadow: 1px 1px 2px 0px rgba(0,0,0,0.2);
width: 88px;
z-index: 2147483647;
font-size: 16px;
line-height: 1.2rem;
}
.v-enter-active, .v-leave-active {
transition-delay: 0.2s;
transition-property: all;
transition-duration: 0.3s;
}
.v-leave-to {
opacity: 0;
transform: translateY(20px);
}
svg {
display: inline-block;
vertical-align: baseline;
width: 1.1em;
height: 0.825em;
position: relative;
top: 1px;
}
</style>

View File

@ -0,0 +1,122 @@
export default {
name: 'NuxtChild',
functional: true,
props: {
nuxtChildKey: {
type: String,
default: ''
},
keepAlive: Boolean,
keepAliveProps: {
type: Object,
default: undefined
}
},
render (_, { parent, data, props }) {
const h = parent.$createElement
data.nuxtChild = true
const _parent = parent
const transitions = parent.$nuxt.nuxt.transitions
const defaultTransition = parent.$nuxt.nuxt.defaultTransition
let depth = 0
while (parent) {
if (parent.$vnode && parent.$vnode.data.nuxtChild) {
depth++
}
parent = parent.$parent
}
data.nuxtChildDepth = depth
const transition = transitions[depth] || defaultTransition
const transitionProps = {}
transitionsKeys.forEach((key) => {
if (typeof transition[key] !== 'undefined') {
transitionProps[key] = transition[key]
}
})
const listeners = {}
listenersKeys.forEach((key) => {
if (typeof transition[key] === 'function') {
listeners[key] = transition[key].bind(_parent)
}
})
if (process.client) {
// Add triggerScroll event on beforeEnter (fix #1376)
const beforeEnter = listeners.beforeEnter
listeners.beforeEnter = (el) => {
// Ensure to trigger scroll event after calling scrollBehavior
window.$nuxt.$nextTick(() => {
window.$nuxt.$emit('triggerScroll')
})
if (beforeEnter) {
return beforeEnter.call(_parent, el)
}
}
}
// make sure that leave is called asynchronous (fix #5703)
if (transition.css === false) {
const leave = listeners.leave
// only add leave listener when user didnt provide one
// or when it misses the done argument
if (!leave || leave.length < 2) {
listeners.leave = (el, done) => {
if (leave) {
leave.call(_parent, el)
}
_parent.$nextTick(done)
}
}
}
let routerView = h('routerView', data)
if (props.keepAlive) {
routerView = h('keep-alive', { props: props.keepAliveProps }, [routerView])
}
return h('transition', {
props: transitionProps,
on: listeners
}, [routerView])
}
}
const transitionsKeys = [
'name',
'mode',
'appear',
'css',
'type',
'duration',
'enterClass',
'leaveClass',
'appearClass',
'enterActiveClass',
'enterActiveClass',
'leaveActiveClass',
'appearActiveClass',
'enterToClass',
'leaveToClass',
'appearToClass'
]
const listenersKeys = [
'beforeEnter',
'enter',
'afterEnter',
'enterCancelled',
'beforeLeave',
'leave',
'afterLeave',
'leaveCancelled',
'beforeAppear',
'appear',
'afterAppear',
'appearCancelled'
]

View File

@ -0,0 +1,98 @@
<template>
<div class="__nuxt-error-page">
<div class="error">
<svg xmlns="http://www.w3.org/2000/svg" width="90" height="90" fill="#DBE1EC" viewBox="0 0 48 48">
<path d="M22 30h4v4h-4zm0-16h4v12h-4zm1.99-10C12.94 4 4 12.95 4 24s8.94 20 19.99 20S44 35.05 44 24 35.04 4 23.99 4zM24 40c-8.84 0-16-7.16-16-16S15.16 8 24 8s16 7.16 16 16-7.16 16-16 16z" />
</svg>
<div class="title">{{ message }}</div>
<p v-if="statusCode === 404" class="description">
<a v-if="typeof $route === 'undefined'" class="error-link" href="/"></a>
<NuxtLink v-else class="error-link" to="/">Back to the home page</NuxtLink>
</p>
<p class="description" v-else>An error occurred while rendering the page. Check developer tools console for details.</p>
<div class="logo">
<a href="https://nuxtjs.org" target="_blank" rel="noopener">Nuxt</a>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'NuxtError',
props: {
error: {
type: Object,
default: null
}
},
computed: {
statusCode () {
return (this.error && this.error.statusCode) || 500
},
message () {
return this.error.message || 'Error'
}
},
head () {
return {
title: this.message,
meta: [
{
name: 'viewport',
content: 'width=device-width,initial-scale=1.0,minimum-scale=1.0'
}
]
}
}
}
</script>
<style>
.__nuxt-error-page {
padding: 1rem;
background: #F7F8FB;
color: #47494E;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
font-family: sans-serif;
font-weight: 100 !important;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
-webkit-font-smoothing: antialiased;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.__nuxt-error-page .error {
max-width: 450px;
}
.__nuxt-error-page .title {
font-size: 1.5rem;
margin-top: 15px;
color: #47494E;
margin-bottom: 8px;
}
.__nuxt-error-page .description {
color: #7F828B;
line-height: 21px;
margin-bottom: 10px;
}
.__nuxt-error-page a {
color: #7F828B !important;
text-decoration: none;
}
.__nuxt-error-page .logo {
position: fixed;
left: 12px;
bottom: 12px;
}
</style>

View File

@ -0,0 +1,98 @@
import Vue from 'vue'
const requestIdleCallback = window.requestIdleCallback ||
function (cb) {
const start = Date.now()
return setTimeout(function () {
cb({
didTimeout: false,
timeRemaining: () => Math.max(0, 50 - (Date.now() - start))
})
}, 1)
}
const cancelIdleCallback = window.cancelIdleCallback || function (id) {
clearTimeout(id)
}
const observer = window.IntersectionObserver && new window.IntersectionObserver((entries) => {
entries.forEach(({ intersectionRatio, target: link }) => {
if (intersectionRatio <= 0 || !link.__prefetch) {
return
}
link.__prefetch()
})
})
export default {
name: 'NuxtLink',
extends: Vue.component('RouterLink'),
props: {
prefetch: {
type: Boolean,
default: false
},
noPrefetch: {
type: Boolean,
default: false
}
},
mounted () {
if (this.prefetch && !this.noPrefetch) {
this.handleId = requestIdleCallback(this.observe, { timeout: 2e3 })
}
},
beforeDestroy () {
cancelIdleCallback(this.handleId)
if (this.__observed) {
observer.unobserve(this.$el)
delete this.$el.__prefetch
}
},
methods: {
observe () {
// If no IntersectionObserver, avoid prefetching
if (!observer) {
return
}
// Add to observer
if (this.shouldPrefetch()) {
this.$el.__prefetch = this.prefetchLink.bind(this)
observer.observe(this.$el)
this.__observed = true
}
},
shouldPrefetch () {
return this.getPrefetchComponents().length > 0
},
canPrefetch () {
const conn = navigator.connection
const hasBadConnection = this.$nuxt.isOffline || (conn && ((conn.effectiveType || '').includes('2g') || conn.saveData))
return !hasBadConnection
},
getPrefetchComponents () {
const ref = this.$router.resolve(this.to, this.$route, this.append)
const Components = ref.resolved.matched.map(r => r.components.default)
return Components.filter(Component => typeof Component === 'function' && !Component.options && !Component.__prefetched)
},
prefetchLink () {
if (!this.canPrefetch()) {
return
}
// Stop observing this link (in case of internet connection changes)
observer.unobserve(this.$el)
const Components = this.getPrefetchComponents()
for (const Component of Components) {
const componentOrPromise = Component()
if (componentOrPromise instanceof Promise) {
componentOrPromise.catch(() => {})
}
Component.__prefetched = true
}
}
}
}

View File

@ -0,0 +1,16 @@
import Vue from 'vue'
export default {
name: 'NuxtLink',
extends: Vue.component('RouterLink'),
props: {
prefetch: {
type: Boolean,
default: false
},
noPrefetch: {
type: Boolean,
default: false
}
}
}

View File

@ -0,0 +1,154 @@
<script>
export default {
name: 'NuxtLoading',
data () {
return {
percent: 0,
show: false,
canSucceed: true,
reversed: false,
skipTimerCount: 0,
rtl: false,
throttle: 200,
duration: 3000,
continuous: false
}
},
computed: {
left () {
if (!this.continuous && !this.rtl) {
return false
}
return this.rtl
? (this.reversed ? '0px' : 'auto')
: (!this.reversed ? '0px' : 'auto')
}
},
beforeDestroy () {
this.clear()
},
methods: {
clear () {
clearInterval(this._timer)
clearTimeout(this._throttle)
this._timer = null
},
start () {
this.clear()
this.percent = 0
this.reversed = false
this.skipTimerCount = 0
this.canSucceed = true
if (this.throttle) {
this._throttle = setTimeout(() => this.startTimer(), this.throttle)
} else {
this.startTimer()
}
return this
},
set (num) {
this.show = true
this.canSucceed = true
this.percent = Math.min(100, Math.max(0, Math.floor(num)))
return this
},
get () {
return this.percent
},
increase (num) {
this.percent = Math.min(100, Math.floor(this.percent + num))
return this
},
decrease (num) {
this.percent = Math.max(0, Math.floor(this.percent - num))
return this
},
pause () {
clearInterval(this._timer)
return this
},
resume () {
this.startTimer()
return this
},
finish () {
this.percent = this.reversed ? 0 : 100
this.hide()
return this
},
hide () {
this.clear()
setTimeout(() => {
this.show = false
this.$nextTick(() => {
this.percent = 0
this.reversed = false
})
}, 500)
return this
},
fail (error) {
this.canSucceed = false
return this
},
startTimer () {
if (!this.show) {
this.show = true
}
if (typeof this._cut === 'undefined') {
this._cut = 10000 / Math.floor(this.duration)
}
this._timer = setInterval(() => {
/**
* When reversing direction skip one timers
* so 0, 100 are displayed for two iterations
* also disable css width transitioning
* which otherwise interferes and shows
* a jojo effect
*/
if (this.skipTimerCount > 0) {
this.skipTimerCount--
return
}
if (this.reversed) {
this.decrease(this._cut)
} else {
this.increase(this._cut)
}
if (this.continuous) {
if (this.percent >= 100) {
this.skipTimerCount = 1
this.reversed = !this.reversed
} else if (this.percent <= 0) {
this.skipTimerCount = 1
this.reversed = !this.reversed
}
}
}, 100)
}
},
render (h) {
let el = h(false)
if (this.show) {
el = h('div', {
staticClass: 'nuxt-progress',
class: {
'nuxt-progress-notransition': this.skipTimerCount > 0,
'nuxt-progress-failed': !this.canSucceed
},
style: {
width: this.percent + '%',
left: this.left
}
})
}
return el
}
}
</script>

View File

@ -0,0 +1,101 @@
import Vue from 'vue'
import { compile } from '../utils'
import NuxtError from '../../layouts/error.vue'
import NuxtChild from './nuxt-child'
export default {
name: 'Nuxt',
components: {
NuxtChild,
NuxtError
},
props: {
nuxtChildKey: {
type: String,
default: undefined
},
keepAlive: Boolean,
keepAliveProps: {
type: Object,
default: undefined
},
name: {
type: String,
default: 'default'
}
},
errorCaptured (error) {
// if we receive and error while showing the NuxtError component
// capture the error and force an immediate update so we re-render
// without the NuxtError component
if (this.displayingNuxtError) {
this.errorFromNuxtError = error
this.$forceUpdate()
}
},
computed: {
routerViewKey () {
// If nuxtChildKey prop is given or current route has children
if (typeof this.nuxtChildKey !== 'undefined' || this.$route.matched.length > 1) {
return this.nuxtChildKey || compile(this.$route.matched[0].path)(this.$route.params)
}
const [matchedRoute] = this.$route.matched
if (!matchedRoute) {
return this.$route.path
}
const Component = matchedRoute.components.default
if (Component && Component.options) {
const { options } = Component
if (options.key) {
return (typeof options.key === 'function' ? options.key(this.$route) : options.key)
}
}
const strict = /\/$/.test(matchedRoute.path)
return strict ? this.$route.path : this.$route.path.replace(/\/$/, '')
}
},
beforeCreate () {
Vue.util.defineReactive(this, 'nuxt', this.$root.$options.nuxt)
},
render (h) {
// if there is no error
if (!this.nuxt.err) {
// Directly return nuxt child
return h('NuxtChild', {
key: this.routerViewKey,
props: this.$props
})
}
// if an error occurred within NuxtError show a simple
// error message instead to prevent looping
if (this.errorFromNuxtError) {
this.$nextTick(() => (this.errorFromNuxtError = false))
return h('div', {}, [
h('h2', 'An error occurred while showing the error page'),
h('p', 'Unfortunately an error occurred and while showing the error page another error occurred'),
h('p', `Error details: ${this.errorFromNuxtError.toString()}`),
h('nuxt-link', { props: { to: '/' } }, 'Go back to home')
])
}
// track if we are showing the NuxtError component
this.displayingNuxtError = true
this.$nextTick(() => (this.displayingNuxtError = false))
return h(NuxtError, {
props: {
error: this.nuxt.err
}
})
}
}

View File

@ -0,0 +1,9 @@
import cookieUniversal from 'cookie-universal'
export default ({ req, res }, inject) => {
const options = {
"alias": "cookies",
"parseJSON": true
}
inject(options.alias, cookieUniversal(req, res, options.parseJSON))
}

1
shell/nuxt/empty.js Normal file
View File

@ -0,0 +1 @@
// This file is intentionally left empty for noop aliases

364
shell/nuxt/index.js Normal file
View File

@ -0,0 +1,364 @@
import Vue from 'vue';
import Vuex from 'vuex';
import Meta from 'vue-meta';
import ClientOnly from 'vue-client-only';
import NoSsr from 'vue-no-ssr';
import { createRouter } from './router.js';
import NuxtChild from './components/nuxt-child.js';
import NuxtError from '../layouts/error.vue';
import Nuxt from './components/nuxt.js';
import App from './App.js';
import { setContext, getLocation, getRouteData, normalizeError } from './utils';
import { createStore } from './store.js';
/* Plugins */
import './portal-vue.js';
import cookieUniversalNuxt from './cookie-universal-nuxt.js';
import axios from './axios.js';
import plugins from '../core/plugins.js';
import pluginsLoader from '../core/plugins-loader.js';
import axiosShell from '../plugins/axios';
import '../plugins/tooltip';
import '../plugins/vue-clipboard2';
import '../plugins/v-select';
import '../plugins/directives';
import '../plugins/transitions';
import '../plugins/vue-js-modal';
import '../plugins/js-yaml';
import '../plugins/resize';
import '../plugins/shortkey';
import '../plugins/i18n';
import '../plugins/global-formatters';
import '../plugins/trim-whitespace';
import '../plugins/extend-router';
import consolePlugin from '../plugins/console';
import intNumber from '../plugins/int-number';
import nuxtClientInit from '../plugins/nuxt-client-init';
import replaceAll from '../plugins/replaceall';
import backButton from '../plugins/back-button';
import plugin from '../plugins/plugin';
import codeMirror from '../plugins/codemirror-loader';
import '../plugins/formatters';
import version from '../plugins/version';
import steveCreateWorker from '../plugins/steve-create-worker';
// Component: <ClientOnly>
Vue.component(ClientOnly.name, ClientOnly);
// TODO: Remove in Nuxt 3: <NoSsr>
Vue.component(NoSsr.name, {
...NoSsr,
render(h, ctx) {
if (process.client && !NoSsr._warned) {
NoSsr._warned = true;
console.warn('<no-ssr> has been deprecated and will be removed in Nuxt 3, please use <client-only> instead');
}
return NoSsr.render(h, ctx);
}
});
// Component: <NuxtChild>
Vue.component(NuxtChild.name, NuxtChild);
Vue.component('NChild', NuxtChild);
// Component NuxtLink is imported in server.js or client.js
// Component: <Nuxt>
Vue.component(Nuxt.name, Nuxt);
Object.defineProperty(Vue.prototype, '$nuxt', {
get() {
const globalNuxt = this.$root.$options.$nuxt;
if (process.client && !globalNuxt && typeof window !== 'undefined') {
return window.$nuxt;
}
return globalNuxt;
},
configurable: true
});
Vue.use(Meta, {
keyName: 'head', attribute: 'data-n-head', ssrAttribute: 'data-n-head-ssr', tagIDKeyName: 'hid'
});
const defaultTransition = {
name: 'page', mode: 'out-in', appear: true, appearClass: 'appear', appearActiveClass: 'appear-active', appearToClass: 'appear-to'
};
const originalRegisterModule = Vuex.Store.prototype.registerModule;
function registerModule(path, rawModule, options = {}) {
const preserveState = process.client && (
Array.isArray(path) ? !!path.reduce((namespacedState, path) => namespacedState && namespacedState[path], this.state) : path in this.state
);
return originalRegisterModule.call(this, path, rawModule, { preserveState, ...options });
}
async function createApp(ssrContext, config = {}) {
const router = await createRouter(ssrContext, config);
const store = createStore(ssrContext);
// Add this.$router into store actions/mutations
store.$router = router;
// Create Root instance
// here we inject the router and store to all child components,
// making them available everywhere as `this.$router` and `this.$store`.
const app = {
head: {"title":"dashboard","meta":[{"charset":"utf-8"},{"name":"viewport","content":"width=device-width, initial-scale=1"},{"hid":"description","name":"description","content":"Rancher Dashboard"}],"link":[{"hid":"icon","rel":"icon","type":"image\u002Fx-icon","href":"\u002Ffavicon.png"}],"style":[],"script":[]},
store,
router,
nuxt: {
defaultTransition,
transitions: [defaultTransition],
setTransitions(transitions) {
if (!Array.isArray(transitions)) {
transitions = [transitions];
}
transitions = transitions.map((transition) => {
if (!transition) {
transition = defaultTransition;
} else if (typeof transition === 'string') {
transition = Object.assign({}, defaultTransition, { name: transition });
} else {
transition = Object.assign({}, defaultTransition, transition);
}
return transition;
});
this.$options.nuxt.transitions = transitions;
return transitions;
},
err: null,
dateErr: null,
error(err) {
err = err || null;
app.context._errored = Boolean(err);
err = err ? normalizeError(err) : null;
let nuxt = app.nuxt; // to work with @vue/composition-api, see https://github.com/nuxt/nuxt.js/issues/6517#issuecomment-573280207
if (this) {
nuxt = this.nuxt || this.$options.nuxt;
}
nuxt.dateErr = Date.now();
nuxt.err = err;
// Used in src/server.js
if (ssrContext) {
ssrContext.nuxt.error = err;
}
return err;
}
},
...App
};
// Make app available into store via this.app
store.app = app;
const next = ssrContext ? ssrContext.next : location => app.router.push(location);
// Resolve route
let route;
if (ssrContext) {
route = router.resolve(ssrContext.url).route;
} else {
const path = getLocation(router.options.base, router.options.mode);
route = router.resolve(path).route;
}
// Set context to app.context
await setContext(app, {
store,
route,
next,
error: app.nuxt.error.bind(app),
payload: ssrContext ? ssrContext.payload : undefined,
req: ssrContext ? ssrContext.req : undefined,
res: ssrContext ? ssrContext.res : undefined,
beforeRenderFns: ssrContext ? ssrContext.beforeRenderFns : undefined,
ssrContext
});
function inject(key, value) {
if (!key) {
throw new Error('inject(key, value) has no key provided');
}
if (value === undefined) {
throw new Error(`inject('${ key }', value) has no value provided`);
}
key = `$${ key }`;
// Add into app
app[key] = value;
// Add into context
if (!app.context[key]) {
app.context[key] = value;
}
// Add into store
store[key] = app[key];
// Check if plugin not already installed
const installKey = `__nuxt_${ key }_installed__`;
if (Vue[installKey]) {
return;
}
Vue[installKey] = true;
// Call Vue.use() to install the plugin into vm
Vue.use(() => {
if (!Object.prototype.hasOwnProperty.call(Vue.prototype, key)) {
Object.defineProperty(Vue.prototype, key, {
get() {
return this.$root.$options[key];
}
});
}
});
}
// Inject runtime config as $config
inject('config', config);
if (process.client) {
// Replace store state before plugins execution
if (window.__NUXT__ && window.__NUXT__.state) {
store.replaceState(window.__NUXT__.state);
}
}
// Add enablePreview(previewData = {}) in context for plugins
if (process.static && process.client) {
app.context.enablePreview = function(previewData = {}) {
app.previewData = Object.assign({}, previewData);
inject('preview', previewData);
};
}
// Plugin execution
// if (typeof nuxt_plugin_portalvue_6babae27 === 'function') {
// await nuxt_plugin_portalvue_6babae27(app.context, inject);
// }
if (typeof cookieUniversalNuxt === 'function') {
await cookieUniversalNuxt(app.context, inject);
}
if (typeof axios === 'function') {
await axios(app.context, inject);
}
if (typeof plugins === 'function') {
await plugins(app.context, inject);
}
if (typeof pluginsLoader === 'function') {
await pluginsLoader(app.context, inject);
}
if (typeof axiosShell === 'function') {
await axiosShell(app.context, inject);
}
if (process.client && typeof consolePlugin === 'function') {
await consolePlugin(app.context, inject);
}
if (process.client && typeof intNumber === 'function') {
await intNumber(app.context, inject);
}
if (process.client && typeof nuxtClientInit === 'function') {
await nuxtClientInit(app.context, inject);
}
if (typeof replaceAll === 'function') {
await replaceAll(app.context, inject);
}
if (typeof backButton === 'function') {
await backButton(app.context, inject);
}
if (process.client && typeof plugin === 'function') {
await plugin(app.context, inject);
}
if (process.client && typeof codeMirror === 'function') {
await codeMirror(app.context, inject);
}
if (process.client && typeof version === 'function') {
await version(app.context, inject);
}
if (process.client && typeof steveCreateWorker === 'function') {
await steveCreateWorker(app.context, inject);
}
// if (process.client && typeof formatters === 'function') {
// await formatters(app.context, inject);
// }
// Lock enablePreview in context
if (process.static && process.client) {
app.context.enablePreview = function() {
console.warn('You cannot call enablePreview() outside a plugin.');
};
}
// Wait for async component to be resolved first
await new Promise((resolve, reject) => {
// Ignore 404s rather than blindly replacing URL in browser
if (process.client) {
const { route } = router.resolve(app.context.route.fullPath);
if (!route.matched.length) {
return resolve();
}
}
router.replace(app.context.route.fullPath, resolve, (err) => {
// https://github.com/vuejs/vue-router/blob/v3.4.3/src/util/errors.js
if (!err._isRouter) {
return reject(err);
}
if (err.type !== 2 /* NavigationFailureType.redirected */) {
return resolve();
}
// navigated to a different route in router guard
const unregister = router.afterEach(async(to, from) => {
if (process.server && ssrContext && ssrContext.url) {
ssrContext.url = to.fullPath;
}
app.context.route = await getRouteData(to);
app.context.params = to.params || {};
app.context.query = to.query || {};
unregister();
resolve();
});
});
});
return {
store,
app,
router
};
}
export { createApp, NuxtError };

82
shell/nuxt/jsonp.js Normal file
View File

@ -0,0 +1,82 @@
const chunks = {} // chunkId => exports
const chunksInstalling = {} // chunkId => Promise
const failedChunks = {}
function importChunk(chunkId, src) {
// Already installed
if (chunks[chunkId]) {
return Promise.resolve(chunks[chunkId])
}
// Failed loading
if (failedChunks[chunkId]) {
return Promise.reject(failedChunks[chunkId])
}
// Installing
if (chunksInstalling[chunkId]) {
return chunksInstalling[chunkId]
}
// Set a promise in chunk cache
let resolve, reject
const promise = chunksInstalling[chunkId] = new Promise((_resolve, _reject) => {
resolve = _resolve
reject = _reject
})
// Clear chunk data from cache
delete chunks[chunkId]
// Start chunk loading
const script = document.createElement('script')
script.charset = 'utf-8'
script.timeout = 120
script.src = src
let timeout
// Create error before stack unwound to get useful stacktrace later
const error = new Error()
// Complete handlers
const onScriptComplete = script.onerror = script.onload = (event) => {
// Cleanups
clearTimeout(timeout)
delete chunksInstalling[chunkId]
// Avoid mem leaks in IE
script.onerror = script.onload = null
// Verify chunk is loaded
if (chunks[chunkId]) {
return resolve(chunks[chunkId])
}
// Something bad happened
const errorType = event && (event.type === 'load' ? 'missing' : event.type)
const realSrc = event && event.target && event.target.src
error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')'
error.name = 'ChunkLoadError'
error.type = errorType
error.request = realSrc
failedChunks[chunkId] = error
reject(error)
}
// Timeout
timeout = setTimeout(() => {
onScriptComplete({ type: 'timeout', target: script })
}, 120000)
// Append script
document.head.appendChild(script)
// Return promise
return promise
}
export function installJsonp() {
window.__NUXT_JSONP__ = function (chunkId, exports) { chunks[chunkId] = exports }
window.__NUXT_JSONP_CACHE__ = chunks
window.__NUXT_IMPORT__ = importChunk
}

39
shell/nuxt/loading.html Normal file
View File

@ -0,0 +1,39 @@
<style>
.initial-load-spinner-container {
align-items: center;
background-color: #f8f8f8;
display: flex;
justify-content: center;
height: 100vh;
left: 0;
position: absolute;
top: 0;
width: 100vw;
}
.initial-load-spinner {
animation: initial-load-animate 1s infinite linear;
background-color: #fff;
box-sizing: border-box;
border: 5px solid #008ACF;
border-radius: 50%;
border-top-color: #00B2E2;
display: inline-block;
height: 80px;
margin: 0 auto;
width: 80px;
}
@keyframes initial-load-animate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(359deg);
}
}
</style>
<div class="initial-load-spinner-container">
<i class="initial-load-spinner"></i>
</div>

12
shell/nuxt/middleware.js Normal file
View File

@ -0,0 +1,12 @@
const middleware = {}
middleware['authenticated'] = require('../middleware/authenticated.js')
middleware['authenticated'] = middleware['authenticated'].default || middleware['authenticated']
middleware['i18n'] = require('../middleware/i18n.js')
middleware['i18n'] = middleware['i18n'].default || middleware['i18n']
middleware['unauthenticated'] = require('../middleware/unauthenticated.js')
middleware['unauthenticated'] = middleware['unauthenticated'].default || middleware['unauthenticated']
export default middleware

View File

@ -0,0 +1,90 @@
import Vue from 'vue'
import { hasFetch, normalizeError, addLifecycleHook, createGetCounter } from '../utils'
const isSsrHydration = (vm) => vm.$vnode && vm.$vnode.elm && vm.$vnode.elm.dataset && vm.$vnode.elm.dataset.fetchKey
const nuxtState = window.__NUXT__
export default {
beforeCreate () {
if (!hasFetch(this)) {
return
}
this._fetchDelay = typeof this.$options.fetchDelay === 'number' ? this.$options.fetchDelay : 200
Vue.util.defineReactive(this, '$fetchState', {
pending: false,
error: null,
timestamp: Date.now()
})
this.$fetch = $fetch.bind(this)
addLifecycleHook(this, 'created', created)
addLifecycleHook(this, 'beforeMount', beforeMount)
}
}
function beforeMount() {
if (!this._hydrated) {
return this.$fetch()
}
}
function created() {
if (!isSsrHydration(this)) {
return
}
// Hydrate component
this._hydrated = true
this._fetchKey = this.$vnode.elm.dataset.fetchKey
const data = nuxtState.fetch[this._fetchKey]
// If fetch error
if (data && data._error) {
this.$fetchState.error = data._error
return
}
// Merge data
for (const key in data) {
Vue.set(this.$data, key, data[key])
}
}
function $fetch() {
if (!this._fetchPromise) {
this._fetchPromise = $_fetch.call(this)
.then(() => { delete this._fetchPromise })
}
return this._fetchPromise
}
async function $_fetch() {
this.$nuxt.nbFetching++
this.$fetchState.pending = true
this.$fetchState.error = null
this._hydrated = false
let error = null
const startTime = Date.now()
try {
await this.$options.fetch.call(this)
} catch (err) {
if (process.dev) {
console.error('Error in fetch():', err)
}
error = normalizeError(err)
}
const delayLeft = this._fetchDelay - (Date.now() - startTime)
if (delayLeft > 0) {
await new Promise(resolve => setTimeout(resolve, delayLeft))
}
this.$fetchState.error = error
this.$fetchState.pending = false
this.$fetchState.timestamp = Date.now()
this.$nextTick(() => this.$nuxt.nbFetching--)
}

View File

@ -0,0 +1,69 @@
import Vue from 'vue'
import { hasFetch, normalizeError, addLifecycleHook, purifyData, createGetCounter } from '../utils'
async function serverPrefetch() {
if (!this._fetchOnServer) {
return
}
// Call and await on $fetch
try {
await this.$options.fetch.call(this)
} catch (err) {
if (process.dev) {
console.error('Error in fetch():', err)
}
this.$fetchState.error = normalizeError(err)
}
this.$fetchState.pending = false
// Define an ssrKey for hydration
this._fetchKey = this._fetchKey || this.$ssrContext.fetchCounters['']++
// Add data-fetch-key on parent element of Component
const attrs = this.$vnode.data.attrs = this.$vnode.data.attrs || {}
attrs['data-fetch-key'] = this._fetchKey
// Add to ssrContext for window.__NUXT__.fetch
if (this.$ssrContext.nuxt.fetch[this._fetchKey] !== undefined) {
console.warn(`Duplicate fetch key detected (${this._fetchKey}). This may lead to unexpected results.`)
}
this.$ssrContext.nuxt.fetch[this._fetchKey] =
this.$fetchState.error ? { _error: this.$fetchState.error } : purifyData(this._data)
}
export default {
created() {
if (!hasFetch(this)) {
return
}
if (typeof this.$options.fetchOnServer === 'function') {
this._fetchOnServer = this.$options.fetchOnServer.call(this) !== false
} else {
this._fetchOnServer = this.$options.fetchOnServer !== false
}
const defaultKey = this.$options._scopeId || this.$options.name || ''
const getCounter = createGetCounter(this.$ssrContext.fetchCounters, defaultKey)
if (typeof this.$options.fetchKey === 'function') {
this._fetchKey = this.$options.fetchKey.call(this, getCounter)
} else {
const key = 'string' === typeof this.$options.fetchKey ? this.$options.fetchKey : defaultKey
this._fetchKey = key ? key + ':' + getCounter(key) : String(getCounter(key))
}
// Added for remove vue undefined warning while ssr
this.$fetch = () => {} // issue #8043
Vue.util.defineReactive(this, '$fetchState', {
pending: true,
error: null,
timestamp: Date.now()
})
addLifecycleHook(this, 'serverPrefetch', serverPrefetch)
}
}

4
shell/nuxt/portal-vue.js Normal file
View File

@ -0,0 +1,4 @@
import Vue from 'vue'
import PortalVue from 'portal-vue'
Vue.use(PortalVue)

503
shell/nuxt/router.js Normal file
View File

@ -0,0 +1,503 @@
import Vue from 'vue'
import Router from 'vue-router'
import { normalizeURL, decode } from 'ufo'
import { interopDefault } from './utils'
import scrollBehavior from './router.scrollBehavior.js'
const _b1075e64 = () => interopDefault(import('../pages/about.vue' /* webpackChunkName: "pages/about" */))
const _99a1a59e = () => interopDefault(import('../pages/account/index.vue' /* webpackChunkName: "pages/account/index" */))
const _10f92ab2 = () => interopDefault(import('../pages/c/index.vue' /* webpackChunkName: "pages/c/index" */))
const _0dd7368b = () => interopDefault(import('../pages/clusters/index.vue' /* webpackChunkName: "pages/clusters/index" */))
const _a0a6a994 = () => interopDefault(import('../pages/diagnostic.vue' /* webpackChunkName: "pages/diagnostic" */))
const _6249d7c9 = () => interopDefault(import('../pages/fail-whale.vue' /* webpackChunkName: "pages/fail-whale" */))
const _8d3a64a4 = () => interopDefault(import('../pages/home.vue' /* webpackChunkName: "pages/home" */))
const _aa26f31e = () => interopDefault(import('../pages/prefs.vue' /* webpackChunkName: "pages/prefs" */))
const _11f3a39f = () => interopDefault(import('../pages/safeMode.vue' /* webpackChunkName: "pages/safeMode" */))
const _35190053 = () => interopDefault(import('../pages/support/index.vue' /* webpackChunkName: "pages/support/index" */))
const _da172982 = () => interopDefault(import('../pages/account/create-key.vue' /* webpackChunkName: "pages/account/create-key" */))
const _35d0be51 = () => interopDefault(import('../pages/auth/login.vue' /* webpackChunkName: "pages/auth/login" */))
const _5d4fd75c = () => interopDefault(import('../pages/auth/logout.vue' /* webpackChunkName: "pages/auth/logout" */))
const _0da94136 = () => interopDefault(import('../pages/auth/setup.vue' /* webpackChunkName: "pages/auth/setup" */))
const _9d01107e = () => interopDefault(import('../pages/auth/verify.vue' /* webpackChunkName: "pages/auth/verify" */))
const _52164cec = () => interopDefault(import('../pages/docs/toc.js' /* webpackChunkName: "pages/docs/toc" */))
const _06776753 = () => interopDefault(import('../pages/rio/mesh.vue' /* webpackChunkName: "pages/rio/mesh" */))
const _2992430e = () => interopDefault(import('../pages/c/_cluster/index.vue' /* webpackChunkName: "pages/c/_cluster/index" */))
const _71a3608e = () => interopDefault(import('../pages/docs/_doc.vue' /* webpackChunkName: "pages/docs/_doc" */))
const _5efe405e = () => interopDefault(import('../pages/c/_cluster/apps/index.vue' /* webpackChunkName: "pages/c/_cluster/apps/index" */))
const _7eff6fd8 = () => interopDefault(import('../pages/c/_cluster/auth/index.vue' /* webpackChunkName: "pages/c/_cluster/auth/index" */))
const _20a6f76e = () => interopDefault(import('../pages/c/_cluster/backup/index.vue' /* webpackChunkName: "pages/c/_cluster/backup/index" */))
const _ece6af92 = () => interopDefault(import('../pages/c/_cluster/cis/index.vue' /* webpackChunkName: "pages/c/_cluster/cis/index" */))
const _89e6b70e = () => interopDefault(import('../pages/c/_cluster/ecm/index.vue' /* webpackChunkName: "pages/c/_cluster/ecm/index" */))
const _0561a16b = () => interopDefault(import('../pages/c/_cluster/explorer/index.vue' /* webpackChunkName: "pages/c/_cluster/explorer/index" */))
const _9f4d6090 = () => interopDefault(import('../pages/c/_cluster/fleet/index.vue' /* webpackChunkName: "pages/c/_cluster/fleet/index" */))
const _0fa0d22e = () => interopDefault(import('../pages/c/_cluster/gatekeeper/index.vue' /* webpackChunkName: "pages/c/_cluster/gatekeeper/index" */))
const _1af88b1a = () => interopDefault(import('../pages/c/_cluster/istio/index.vue' /* webpackChunkName: "pages/c/_cluster/istio/index" */))
const _3facd8b5 = () => interopDefault(import('../pages/c/_cluster/legacy/index.vue' /* webpackChunkName: "pages/c/_cluster/legacy/index" */))
const _24bc84c9 = () => interopDefault(import('../pages/c/_cluster/logging/index.vue' /* webpackChunkName: "pages/c/_cluster/logging/index" */))
const _22d2372b = () => interopDefault(import('../pages/c/_cluster/longhorn/index.vue' /* webpackChunkName: "pages/c/_cluster/longhorn/index" */))
const _e66f80d2 = () => interopDefault(import('../pages/c/_cluster/manager/index.vue' /* webpackChunkName: "pages/c/_cluster/manager/index" */))
const _02abbf34 = () => interopDefault(import('../pages/c/_cluster/mcapps/index.vue' /* webpackChunkName: "pages/c/_cluster/mcapps/index" */))
const _e1577418 = () => interopDefault(import('../pages/c/_cluster/monitoring/index.vue' /* webpackChunkName: "pages/c/_cluster/monitoring/index" */))
const _4954338b = () => interopDefault(import('../pages/c/_cluster/neuvector/index.vue' /* webpackChunkName: "pages/c/_cluster/neuvector/index" */))
const _86270f62 = () => interopDefault(import('../pages/c/_cluster/settings/index.vue' /* webpackChunkName: "pages/c/_cluster/settings/index" */))
const _0afff7f6 = () => interopDefault(import('../pages/c/_cluster/uiplugins/index.vue' /* webpackChunkName: "pages/c/_cluster/uiplugins/index" */))
const _3cd56cbc = () => interopDefault(import('../pages/c/_cluster/apps/charts/index.vue' /* webpackChunkName: "pages/c/_cluster/apps/charts/index" */))
const _11b0721a = () => interopDefault(import('../pages/c/_cluster/auth/config/index.vue' /* webpackChunkName: "pages/c/_cluster/auth/config/index" */))
const _25aea87c = () => interopDefault(import('../pages/c/_cluster/auth/roles/index.vue' /* webpackChunkName: "pages/c/_cluster/auth/roles/index" */))
const _74341dba = () => interopDefault(import('../pages/c/_cluster/explorer/ConfigBadge.vue' /* webpackChunkName: "pages/c/_cluster/explorer/ConfigBadge" */))
const _14bdf46e = () => interopDefault(import('../pages/c/_cluster/explorer/EventsTable.vue' /* webpackChunkName: "pages/c/_cluster/explorer/EventsTable" */))
const _a75fe116 = () => interopDefault(import('../pages/c/_cluster/explorer/explorer-utils.js' /* webpackChunkName: "pages/c/_cluster/explorer/explorer-utils" */))
const _01865512 = () => interopDefault(import('../pages/c/_cluster/explorer/tools/index.vue' /* webpackChunkName: "pages/c/_cluster/explorer/tools/index" */))
const _9c418d0e = () => interopDefault(import('../pages/c/_cluster/fleet/GitRepoGraphConfig.js' /* webpackChunkName: "pages/c/_cluster/fleet/GitRepoGraphConfig" */))
const _f1812060 = () => interopDefault(import('../pages/c/_cluster/gatekeeper/constraints/index.vue' /* webpackChunkName: "pages/c/_cluster/gatekeeper/constraints/index" */))
const _b539bb82 = () => interopDefault(import('../pages/c/_cluster/legacy/project/index.vue' /* webpackChunkName: "pages/c/_cluster/legacy/project/index" */))
const _44fb97b4 = () => interopDefault(import('../pages/c/_cluster/manager/cloudCredential/index.vue' /* webpackChunkName: "pages/c/_cluster/manager/cloudCredential/index" */))
const _17ce10e4 = () => interopDefault(import('../pages/c/_cluster/monitoring/alertmanagerconfig/index.vue' /* webpackChunkName: "pages/c/_cluster/monitoring/alertmanagerconfig/index" */))
const _57f0357f = () => interopDefault(import('../pages/c/_cluster/monitoring/monitor/index.vue' /* webpackChunkName: "pages/c/_cluster/monitoring/monitor/index" */))
const _acf430f8 = () => interopDefault(import('../pages/c/_cluster/monitoring/route-receiver/index.vue' /* webpackChunkName: "pages/c/_cluster/monitoring/route-receiver/index" */))
const _9c1862f8 = () => interopDefault(import('../pages/c/_cluster/settings/banners.vue' /* webpackChunkName: "pages/c/_cluster/settings/banners" */))
const _83bd8ef8 = () => interopDefault(import('../pages/c/_cluster/settings/brand.vue' /* webpackChunkName: "pages/c/_cluster/settings/brand" */))
const _6ace98ec = () => interopDefault(import('../pages/c/_cluster/settings/DefaultLinksEditor.vue' /* webpackChunkName: "shell/pages/c/_cluster/settings/DefaultLinksEditor" */))
const _e56e5894 = () => interopDefault(import('../pages/c/_cluster/settings/links.vue' /* webpackChunkName: "pages/c/_cluster/settings/links" */))
const _0ff4c0ed = () => interopDefault(import('../pages/c/_cluster/settings/performance.vue' /* webpackChunkName: "pages/c/_cluster/settings/performance" */))
const _978f0576 = () => interopDefault(import('../pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue' /* webpackChunkName: "pages/c/_cluster/uiplugins/DeveloperInstallDialog" */))
const _256d9147 = () => interopDefault(import('../pages/c/_cluster/uiplugins/InstallDialog.vue' /* webpackChunkName: "pages/c/_cluster/uiplugins/InstallDialog" */))
const _f6d8b8f2 = () => interopDefault(import('../pages/c/_cluster/uiplugins/PluginInfoPanel.vue' /* webpackChunkName: "pages/c/_cluster/uiplugins/PluginInfoPanel" */))
const _33e16f6c = () => interopDefault(import('../pages/c/_cluster/uiplugins/RemoveUIPlugins.vue' /* webpackChunkName: "pages/c/_cluster/uiplugins/RemoveUIPlugins" */))
const _d7c9a08a = () => interopDefault(import('../pages/c/_cluster/uiplugins/SetupUIPlugins.vue' /* webpackChunkName: "pages/c/_cluster/uiplugins/SetupUIPlugins" */))
const _266ebcce = () => interopDefault(import('../pages/c/_cluster/uiplugins/UninstallDialog.vue' /* webpackChunkName: "pages/c/_cluster/uiplugins/UninstallDialog" */))
const _69909470 = () => interopDefault(import('../pages/c/_cluster/apps/charts/chart.vue' /* webpackChunkName: "pages/c/_cluster/apps/charts/chart" */))
const _685cdef6 = () => interopDefault(import('../pages/c/_cluster/apps/charts/install.vue' /* webpackChunkName: "pages/c/_cluster/apps/charts/install" */))
const _5e92cb4c = () => interopDefault(import('../pages/c/_cluster/auth/group.principal/assign-edit.vue' /* webpackChunkName: "pages/c/_cluster/auth/group.principal/assign-edit" */))
const _97480d04 = () => interopDefault(import('../pages/c/_cluster/legacy/project/pipelines.vue' /* webpackChunkName: "pages/c/_cluster/legacy/project/pipelines" */))
const _fba8acec = () => interopDefault(import('../pages/c/_cluster/manager/cloudCredential/create.vue' /* webpackChunkName: "pages/c/_cluster/manager/cloudCredential/create" */))
const _646a75c2 = () => interopDefault(import('../pages/c/_cluster/monitoring/monitor/create.vue' /* webpackChunkName: "pages/c/_cluster/monitoring/monitor/create" */))
const _a229588c = () => interopDefault(import('../pages/c/_cluster/monitoring/route-receiver/create.vue' /* webpackChunkName: "pages/c/_cluster/monitoring/route-receiver/create" */))
const _24eaabc8 = () => interopDefault(import('../pages/c/_cluster/explorer/tools/pages/_page.vue' /* webpackChunkName: "pages/c/_cluster/explorer/tools/pages/_page" */))
const _77d0ea1b = () => interopDefault(import('../pages/c/_cluster/auth/config/_id.vue' /* webpackChunkName: "pages/c/_cluster/auth/config/_id" */))
const _58626ef4 = () => interopDefault(import('../pages/c/_cluster/legacy/pages/_page.vue' /* webpackChunkName: "pages/c/_cluster/legacy/pages/_page" */))
const _0e2466db = () => interopDefault(import('../pages/c/_cluster/legacy/project/_page.vue' /* webpackChunkName: "pages/c/_cluster/legacy/project/_page" */))
const _5b9811c8 = () => interopDefault(import('../pages/c/_cluster/manager/cloudCredential/_id.vue' /* webpackChunkName: "pages/c/_cluster/manager/cloudCredential/_id" */))
const _0f85fde8 = () => interopDefault(import('../pages/c/_cluster/manager/pages/_page.vue' /* webpackChunkName: "pages/c/_cluster/manager/pages/_page" */))
const _107ed876 = () => interopDefault(import('../pages/c/_cluster/mcapps/pages/_page.vue' /* webpackChunkName: "pages/c/_cluster/mcapps/pages/_page" */))
const _f12c4b3c = () => interopDefault(import('../pages/c/_cluster/monitoring/alertmanagerconfig/_alertmanagerconfigid/index.vue' /* webpackChunkName: "pages/c/_cluster/monitoring/alertmanagerconfig/_alertmanagerconfigid/index" */))
const _25affaec = () => interopDefault(import('../pages/c/_cluster/monitoring/route-receiver/_id.vue' /* webpackChunkName: "pages/c/_cluster/monitoring/route-receiver/_id" */))
const _0e9569c4 = () => interopDefault(import('../pages/c/_cluster/auth/roles/_resource/create.vue' /* webpackChunkName: "pages/c/_cluster/auth/roles/_resource/create" */))
const _0d1a9be2 = () => interopDefault(import('../pages/c/_cluster/monitoring/alertmanagerconfig/_alertmanagerconfigid/receiver.vue' /* webpackChunkName: "pages/c/_cluster/monitoring/alertmanagerconfig/_alertmanagerconfigid/receiver" */))
const _1643de08 = () => interopDefault(import('../pages/c/_cluster/auth/roles/_resource/_id.vue' /* webpackChunkName: "pages/c/_cluster/auth/roles/_resource/_id" */))
const _30293caa = () => interopDefault(import('../pages/c/_cluster/monitoring/monitor/_namespace/_id.vue' /* webpackChunkName: "pages/c/_cluster/monitoring/monitor/_namespace/_id" */))
const _3dcc28a0 = () => interopDefault(import('../pages/c/_cluster/navlinks/_group.vue' /* webpackChunkName: "pages/c/_cluster/navlinks/_group" */))
const _327638c8 = () => interopDefault(import('../pages/c/_cluster/_product/index.vue' /* webpackChunkName: "pages/c/_cluster/_product/index" */))
const _3feb57b4 = () => interopDefault(import('../pages/c/_cluster/_product/members/index.vue' /* webpackChunkName: "pages/c/_cluster/_product/members/index" */))
const _2e2d89c4 = () => interopDefault(import('../pages/c/_cluster/_product/namespaces.vue' /* webpackChunkName: "pages/c/_cluster/_product/namespaces" */))
const _76d90818 = () => interopDefault(import('../pages/c/_cluster/_product/projectsnamespaces.vue' /* webpackChunkName: "pages/c/_cluster/_product/projectsnamespaces" */))
const _587122fa = () => interopDefault(import('../pages/c/_cluster/_product/_resource/index.vue' /* webpackChunkName: "pages/c/_cluster/_product/_resource/index" */))
const _4530f1f8 = () => interopDefault(import('../pages/c/_cluster/_product/_resource/create.vue' /* webpackChunkName: "pages/c/_cluster/_product/_resource/create" */))
const _ac00c83c = () => interopDefault(import('../pages/c/_cluster/_product/_resource/_id.vue' /* webpackChunkName: "pages/c/_cluster/_product/_resource/_id" */))
const _1cac498f = () => interopDefault(import('../pages/c/_cluster/_product/_resource/_namespace/_id.vue' /* webpackChunkName: "pages/c/_cluster/_product/_resource/_namespace/_id" */))
const _7197a8da = () => interopDefault(import('../pages/index.vue' /* webpackChunkName: "pages/index" */))
const emptyFn = () => {}
Vue.use(Router)
export const routerOptions = {
mode: 'history',
base: '/',
linkActiveClass: 'nuxt-link-active',
linkExactActiveClass: 'nuxt-link-exact-active',
scrollBehavior,
routes: [{
path: "/about",
component: _b1075e64,
name: "about"
}, {
path: "/account",
component: _99a1a59e,
name: "account"
}, {
path: "/c",
component: _10f92ab2,
name: "c"
}, {
path: "/clusters",
component: _0dd7368b,
name: "clusters"
}, {
path: "/diagnostic",
component: _a0a6a994,
name: "diagnostic"
}, {
path: "/fail-whale",
component: _6249d7c9,
name: "fail-whale"
}, {
path: "/home",
component: _8d3a64a4,
name: "home"
}, {
path: "/prefs",
component: _aa26f31e,
name: "prefs"
}, {
path: "/safeMode",
component: _11f3a39f,
name: "safeMode"
}, {
path: "/support",
component: _35190053,
name: "support"
}, {
path: "/account/create-key",
component: _da172982,
name: "account-create-key"
}, {
path: "/auth/login",
component: _35d0be51,
name: "auth-login"
}, {
path: "/auth/logout",
component: _5d4fd75c,
name: "auth-logout"
}, {
path: "/auth/setup",
component: _0da94136,
name: "auth-setup"
}, {
path: "/auth/verify",
component: _9d01107e,
name: "auth-verify"
}, {
path: "/docs/toc",
component: _52164cec,
name: "docs-toc"
}, {
path: "/rio/mesh",
component: _06776753,
name: "rio-mesh"
}, {
path: "/c/:cluster",
component: _2992430e,
name: "c-cluster"
}, {
path: "/docs/:doc?",
component: _71a3608e,
name: "docs-doc"
}, {
path: "/c/:cluster/apps",
component: _5efe405e,
name: "c-cluster-apps"
}, {
path: "/c/:cluster/auth",
component: _7eff6fd8,
name: "c-cluster-auth"
}, {
path: "/c/:cluster/backup",
component: _20a6f76e,
name: "c-cluster-backup"
}, {
path: "/c/:cluster/cis",
component: _ece6af92,
name: "c-cluster-cis"
}, {
path: "/c/:cluster/ecm",
component: _89e6b70e,
name: "c-cluster-ecm"
}, {
path: "/c/:cluster/explorer",
component: _0561a16b,
name: "c-cluster-explorer"
}, {
path: "/c/:cluster/fleet",
component: _9f4d6090,
name: "c-cluster-fleet"
}, {
path: "/c/:cluster/gatekeeper",
component: _0fa0d22e,
name: "c-cluster-gatekeeper"
}, {
path: "/c/:cluster/istio",
component: _1af88b1a,
name: "c-cluster-istio"
}, {
path: "/c/:cluster/legacy",
component: _3facd8b5,
name: "c-cluster-legacy"
}, {
path: "/c/:cluster/logging",
component: _24bc84c9,
name: "c-cluster-logging"
}, {
path: "/c/:cluster/longhorn",
component: _22d2372b,
name: "c-cluster-longhorn"
}, {
path: "/c/:cluster/manager",
component: _e66f80d2,
name: "c-cluster-manager"
}, {
path: "/c/:cluster/mcapps",
component: _02abbf34,
name: "c-cluster-mcapps"
}, {
path: "/c/:cluster/monitoring",
component: _e1577418,
name: "c-cluster-monitoring"
}, {
path: "/c/:cluster/neuvector",
component: _4954338b,
name: "c-cluster-neuvector"
}, {
path: "/c/:cluster/settings",
component: _86270f62,
name: "c-cluster-settings"
}, {
path: "/c/:cluster/uiplugins",
component: _0afff7f6,
name: "c-cluster-uiplugins"
}, {
path: "/c/:cluster/apps/charts",
component: _3cd56cbc,
name: "c-cluster-apps-charts"
}, {
path: "/c/:cluster/auth/config",
component: _11b0721a,
name: "c-cluster-auth-config"
}, {
path: "/c/:cluster/auth/roles",
component: _25aea87c,
name: "c-cluster-auth-roles"
}, {
path: "/c/:cluster/explorer/ConfigBadge",
component: _74341dba,
name: "c-cluster-explorer-ConfigBadge"
}, {
path: "/c/:cluster/explorer/EventsTable",
component: _14bdf46e,
name: "c-cluster-explorer-EventsTable"
}, {
path: "/c/:cluster/explorer/explorer-utils",
component: _a75fe116,
name: "c-cluster-explorer-explorer-utils"
}, {
path: "/c/:cluster/explorer/tools",
component: _01865512,
name: "c-cluster-explorer-tools"
}, {
path: "/c/:cluster/fleet/GitRepoGraphConfig",
component: _9c418d0e,
name: "c-cluster-fleet-GitRepoGraphConfig"
}, {
path: "/c/:cluster/gatekeeper/constraints",
component: _f1812060,
name: "c-cluster-gatekeeper-constraints"
}, {
path: "/c/:cluster/legacy/project",
component: _b539bb82,
name: "c-cluster-legacy-project"
}, {
path: "/c/:cluster/manager/cloudCredential",
component: _44fb97b4,
name: "c-cluster-manager-cloudCredential"
}, {
path: "/c/:cluster/monitoring/alertmanagerconfig",
component: _17ce10e4,
name: "c-cluster-monitoring-alertmanagerconfig"
}, {
path: "/c/:cluster/monitoring/monitor",
component: _57f0357f,
name: "c-cluster-monitoring-monitor"
}, {
path: "/c/:cluster/monitoring/route-receiver",
component: _acf430f8,
name: "c-cluster-monitoring-route-receiver"
}, {
path: "/c/:cluster/settings/banners",
component: _9c1862f8,
name: "c-cluster-settings-banners"
}, {
path: "/c/:cluster/settings/brand",
component: _83bd8ef8,
name: "c-cluster-settings-brand"
}, {
path: "/c/:cluster/settings/DefaultLinksEditor",
component: _6ace98ec,
name: "c-cluster-settings-DefaultLinksEditor"
}, {
path: "/c/:cluster/settings/links",
component: _e56e5894,
name: "c-cluster-settings-links"
}, {
path: "/c/:cluster/settings/performance",
component: _0ff4c0ed,
name: "c-cluster-settings-performance"
}, {
path: "/c/:cluster/uiplugins/DeveloperInstallDialog",
component: _978f0576,
name: "c-cluster-uiplugins-DeveloperInstallDialog"
}, {
path: "/c/:cluster/uiplugins/InstallDialog",
component: _256d9147,
name: "c-cluster-uiplugins-InstallDialog"
}, {
path: "/c/:cluster/uiplugins/PluginInfoPanel",
component: _f6d8b8f2,
name: "c-cluster-uiplugins-PluginInfoPanel"
}, {
path: "/c/:cluster/uiplugins/RemoveUIPlugins",
component: _33e16f6c,
name: "c-cluster-uiplugins-RemoveUIPlugins"
}, {
path: "/c/:cluster/uiplugins/SetupUIPlugins",
component: _d7c9a08a,
name: "c-cluster-uiplugins-SetupUIPlugins"
}, {
path: "/c/:cluster/uiplugins/UninstallDialog",
component: _266ebcce,
name: "c-cluster-uiplugins-UninstallDialog"
}, {
path: "/c/:cluster/apps/charts/chart",
component: _69909470,
name: "c-cluster-apps-charts-chart"
}, {
path: "/c/:cluster/apps/charts/install",
component: _685cdef6,
name: "c-cluster-apps-charts-install"
}, {
path: "/c/:cluster/auth/group.principal/assign-edit",
component: _5e92cb4c,
name: "c-cluster-auth-group.principal-assign-edit"
}, {
path: "/c/:cluster/legacy/project/pipelines",
component: _97480d04,
name: "c-cluster-legacy-project-pipelines"
}, {
path: "/c/:cluster/manager/cloudCredential/create",
component: _fba8acec,
name: "c-cluster-manager-cloudCredential-create"
}, {
path: "/c/:cluster/monitoring/monitor/create",
component: _646a75c2,
name: "c-cluster-monitoring-monitor-create"
}, {
path: "/c/:cluster/monitoring/route-receiver/create",
component: _a229588c,
name: "c-cluster-monitoring-route-receiver-create"
}, {
path: "/c/:cluster/explorer/tools/pages/:page?",
component: _24eaabc8,
name: "c-cluster-explorer-tools-pages-page"
}, {
path: "/c/:cluster/auth/config/:id",
component: _77d0ea1b,
name: "c-cluster-auth-config-id"
}, {
path: "/c/:cluster/legacy/pages/:page?",
component: _58626ef4,
name: "c-cluster-legacy-pages-page"
}, {
path: "/c/:cluster/legacy/project/:page",
component: _0e2466db,
name: "c-cluster-legacy-project-page"
}, {
path: "/c/:cluster/manager/cloudCredential/:id",
component: _5b9811c8,
name: "c-cluster-manager-cloudCredential-id"
}, {
path: "/c/:cluster/manager/pages/:page?",
component: _0f85fde8,
name: "c-cluster-manager-pages-page"
}, {
path: "/c/:cluster/mcapps/pages/:page?",
component: _107ed876,
name: "c-cluster-mcapps-pages-page"
}, {
path: "/c/:cluster/monitoring/alertmanagerconfig/:alertmanagerconfigid",
component: _f12c4b3c,
name: "c-cluster-monitoring-alertmanagerconfig-alertmanagerconfigid"
}, {
path: "/c/:cluster/monitoring/route-receiver/:id?",
component: _25affaec,
name: "c-cluster-monitoring-route-receiver-id"
}, {
path: "/c/:cluster/auth/roles/:resource/create",
component: _0e9569c4,
name: "c-cluster-auth-roles-resource-create"
}, {
path: "/c/:cluster/monitoring/alertmanagerconfig/:alertmanagerconfigid/receiver",
component: _0d1a9be2,
name: "c-cluster-monitoring-alertmanagerconfig-alertmanagerconfigid-receiver"
}, {
path: "/c/:cluster/auth/roles/:resource/:id?",
component: _1643de08,
name: "c-cluster-auth-roles-resource-id"
}, {
path: "/c/:cluster/monitoring/monitor/:namespace/:id?",
component: _30293caa,
name: "c-cluster-monitoring-monitor-namespace-id"
}, {
path: "/c/:cluster/navlinks/:group?",
component: _3dcc28a0,
name: "c-cluster-navlinks-group"
}, {
path: "/c/:cluster/:product",
component: _327638c8,
name: "c-cluster-product"
}, {
path: "/c/:cluster/:product/members",
component: _3feb57b4,
name: "c-cluster-product-members"
}, {
path: "/c/:cluster/:product/namespaces",
component: _2e2d89c4,
name: "c-cluster-product-namespaces"
}, {
path: "/c/:cluster/:product/projectsnamespaces",
component: _76d90818,
name: "c-cluster-product-projectsnamespaces"
}, {
path: "/c/:cluster/:product/:resource",
component: _587122fa,
name: "c-cluster-product-resource"
}, {
path: "/c/:cluster/:product/:resource/create",
component: _4530f1f8,
name: "c-cluster-product-resource-create"
}, {
path: "/c/:cluster/:product/:resource/:id",
component: _ac00c83c,
name: "c-cluster-product-resource-id"
}, {
path: "/c/:cluster/:product/:resource/:namespace/:id?",
component: _1cac498f,
name: "c-cluster-product-resource-namespace-id"
}, {
path: "/",
component: _7197a8da,
name: "index"
}],
fallback: false
}
export function createRouter (ssrContext, config) {
const base = (config._app && config._app.basePath) || routerOptions.base
const router = new Router({ ...routerOptions, base })
// TODO: remove in Nuxt 3
const originalPush = router.push
router.push = function push (location, onComplete = emptyFn, onAbort) {
return originalPush.call(this, location, onComplete, onAbort)
}
const resolve = router.resolve.bind(router)
router.resolve = (to, current, append) => {
if (typeof to === 'string') {
to = normalizeURL(to)
}
return resolve(to, current, append)
}
return router
}

View File

@ -0,0 +1,76 @@
import { getMatchedComponents, setScrollRestoration } from './utils'
if (process.client) {
if ('scrollRestoration' in window.history) {
setScrollRestoration('manual')
// reset scrollRestoration to auto when leaving page, allowing page reload
// and back-navigation from other pages to use the browser to restore the
// scrolling position.
window.addEventListener('beforeunload', () => {
setScrollRestoration('auto')
})
// Setting scrollRestoration to manual again when returning to this page.
window.addEventListener('load', () => {
setScrollRestoration('manual')
})
}
}
function shouldScrollToTop(route) {
const Pages = getMatchedComponents(route)
if (Pages.length === 1) {
const { options = {} } = Pages[0]
return options.scrollToTop !== false
}
return Pages.some(({ options }) => options && options.scrollToTop)
}
export default function (to, from, savedPosition) {
// If the returned position is falsy or an empty object, will retain current scroll position
let position = false
const isRouteChanged = to !== from
// savedPosition is only available for popstate navigations (back button)
if (savedPosition) {
position = savedPosition
} else if (isRouteChanged && shouldScrollToTop(to)) {
position = { x: 0, y: 0 }
}
const nuxt = window.$nuxt
if (
// Initial load (vuejs/vue-router#3199)
!isRouteChanged ||
// Route hash changes
(to.path === from.path && to.hash !== from.hash)
) {
nuxt.$nextTick(() => nuxt.$emit('triggerScroll'))
}
return new Promise((resolve) => {
// wait for the out transition to complete (if necessary)
nuxt.$once('triggerScroll', () => {
// coords will be used if no selector is provided,
// or if the selector didn't match any element.
if (to.hash) {
let hash = to.hash
// CSS.escape() is not supported with IE and Edge.
if (typeof window.CSS !== 'undefined' && typeof window.CSS.escape !== 'undefined') {
hash = '#' + window.CSS.escape(hash.substr(1))
}
try {
if (document.querySelector(hash)) {
// scroll to anchor by returning the selector
position = { selector: hash }
}
} catch (e) {
console.warn('Failed to save scroll position. Please add CSS.escape() polyfill (https://github.com/mathiasbynens/CSS.escape).')
}
}
resolve(position)
})
})
}

312
shell/nuxt/server.js Normal file
View File

@ -0,0 +1,312 @@
import Vue from 'vue'
import { joinURL, normalizeURL, withQuery } from 'ufo'
import fetch from 'node-fetch'
import middleware from './middleware.js'
import {
applyAsyncData,
middlewareSeries,
sanitizeComponent,
getMatchedComponents,
promisify
} from './utils.js'
import fetchMixin from './mixins/fetch.server'
import { createApp, NuxtError } from './index.js'
import NuxtLink from './components/nuxt-link.server.js' // should be included after ./index.js
// Update serverPrefetch strategy
Vue.config.optionMergeStrategies.serverPrefetch = Vue.config.optionMergeStrategies.created
// Fetch mixin
if (!Vue.__nuxt__fetch__mixin__) {
Vue.mixin(fetchMixin)
Vue.__nuxt__fetch__mixin__ = true
}
if (!Vue.__original_use__) {
Vue.__original_use__ = Vue.use
Vue.__install_times__ = 0
Vue.use = function (plugin, ...args) {
plugin.__nuxt_external_installed__ = Vue._installedPlugins.includes(plugin)
return Vue.__original_use__(plugin, ...args)
}
}
if (Vue.__install_times__ === 2) {
Vue.__install_times__ = 0
Vue._installedPlugins = Vue._installedPlugins.filter(plugin => {
return plugin.__nuxt_external_installed__ === true
})
}
Vue.__install_times__++
// Component: <NuxtLink>
Vue.component(NuxtLink.name, NuxtLink)
Vue.component('NLink', NuxtLink)
if (!global.fetch) { global.fetch = fetch }
const noopApp = () => new Vue({ render: h => h('div', { domProps: { id: '__nuxt' } }) })
const createNext = ssrContext => (opts) => {
// If static target, render on client-side
ssrContext.redirected = opts
if (ssrContext.target === 'static' || !ssrContext.res) {
ssrContext.nuxt.serverRendered = false
return
}
let fullPath = withQuery(opts.path, opts.query)
const $config = ssrContext.runtimeConfig || {}
const routerBase = ($config._app && $config._app.basePath) || '/'
if (!fullPath.startsWith('http') && (routerBase !== '/' && !fullPath.startsWith(routerBase))) {
fullPath = joinURL(routerBase, fullPath)
}
// Avoid loop redirect
if (decodeURI(fullPath) === decodeURI(ssrContext.url)) {
ssrContext.redirected = false
return
}
ssrContext.res.writeHead(opts.status, {
Location: normalizeURL(fullPath)
})
ssrContext.res.end()
}
// This exported function will be called by `bundleRenderer`.
// This is where we perform data-prefetching to determine the
// state of our application before actually rendering it.
// Since data fetching is async, this function is expected to
// return a Promise that resolves to the app instance.
export default async (ssrContext) => {
// Create ssrContext.next for simulate next() of beforeEach() when wanted to redirect
ssrContext.redirected = false
ssrContext.next = createNext(ssrContext)
// Used for beforeNuxtRender({ Components, nuxtState })
ssrContext.beforeRenderFns = []
// Nuxt object (window.{{globals.context}}, defaults to window.__NUXT__)
ssrContext.nuxt = { layout: 'default', data: [], fetch: {}, error: null, state: null, serverRendered: true, routePath: '' }
ssrContext.fetchCounters = {}
// Remove query from url is static target
// Public runtime config
ssrContext.nuxt.config = ssrContext.runtimeConfig.public
if (ssrContext.nuxt.config._app) {
__webpack_public_path__ = joinURL(ssrContext.nuxt.config._app.cdnURL, ssrContext.nuxt.config._app.assetsPath)
}
// Create the app definition and the instance (created for each request)
const { app, router, store } = await createApp(ssrContext, ssrContext.runtimeConfig.private)
const _app = new Vue(app)
// Add ssr route path to nuxt context so we can account for page navigation between ssr and csr
ssrContext.nuxt.routePath = app.context.route.path
// Add meta infos (used in renderer.js)
ssrContext.meta = _app.$meta()
// Keep asyncData for each matched component in ssrContext (used in app/utils.js via this.$ssrContext)
ssrContext.asyncData = {}
const beforeRender = async () => {
// Call beforeNuxtRender() methods
await Promise.all(ssrContext.beforeRenderFns.map(fn => promisify(fn, { Components, nuxtState: ssrContext.nuxt })))
ssrContext.rendered = () => {
// Add the state from the vuex store
ssrContext.nuxt.state = store.state
}
}
const renderErrorPage = async () => {
// Don't server-render the page in static target
if (ssrContext.target === 'static') {
ssrContext.nuxt.serverRendered = false
}
// Load layout for error page
const layout = (NuxtError.options || NuxtError).layout
const errLayout = typeof layout === 'function' ? layout.call(NuxtError, app.context) : layout
ssrContext.nuxt.layout = errLayout || 'default'
await _app.loadLayout(errLayout)
_app.setLayout(errLayout)
await beforeRender()
return _app
}
const render404Page = () => {
app.context.error({ statusCode: 404, path: ssrContext.url, message: 'This page could not be found' })
return renderErrorPage()
}
const s = Date.now()
// Components are already resolved by setContext -> getRouteData (app/utils.js)
const Components = getMatchedComponents(app.context.route)
/*
** Dispatch store nuxtServerInit
*/
if (store._actions && store._actions.nuxtServerInit) {
try {
await store.dispatch('nuxtServerInit', app.context)
} catch (err) {
console.debug('Error occurred when calling nuxtServerInit: ', err.message)
throw err
}
}
// ...If there is a redirect or an error, stop the process
if (ssrContext.redirected) {
return noopApp()
}
if (ssrContext.nuxt.error) {
return renderErrorPage()
}
/*
** Call global middleware (nuxt.config.js)
*/
let midd = ["i18n"]
midd = midd.map((name) => {
if (typeof name === 'function') {
return name
}
if (typeof middleware[name] !== 'function') {
app.context.error({ statusCode: 500, message: 'Unknown middleware ' + name })
}
return middleware[name]
})
await middlewareSeries(midd, app.context)
// ...If there is a redirect or an error, stop the process
if (ssrContext.redirected) {
return noopApp()
}
if (ssrContext.nuxt.error) {
return renderErrorPage()
}
/*
** Set layout
*/
let layout = Components.length ? Components[0].options.layout : NuxtError.layout
if (typeof layout === 'function') {
layout = layout(app.context)
}
await _app.loadLayout(layout)
if (ssrContext.nuxt.error) {
return renderErrorPage()
}
layout = _app.setLayout(layout)
ssrContext.nuxt.layout = _app.layoutName
/*
** Call middleware (layout + pages)
*/
midd = []
layout = sanitizeComponent(layout)
if (layout.options.middleware) {
midd = midd.concat(layout.options.middleware)
}
Components.forEach((Component) => {
if (Component.options.middleware) {
midd = midd.concat(Component.options.middleware)
}
})
midd = midd.map((name) => {
if (typeof name === 'function') {
return name
}
if (typeof middleware[name] !== 'function') {
app.context.error({ statusCode: 500, message: 'Unknown middleware ' + name })
}
return middleware[name]
})
await middlewareSeries(midd, app.context)
// ...If there is a redirect or an error, stop the process
if (ssrContext.redirected) {
return noopApp()
}
if (ssrContext.nuxt.error) {
return renderErrorPage()
}
/*
** Call .validate()
*/
let isValid = true
try {
for (const Component of Components) {
if (typeof Component.options.validate !== 'function') {
continue
}
isValid = await Component.options.validate(app.context)
if (!isValid) {
break
}
}
} catch (validationError) {
// ...If .validate() threw an error
app.context.error({
statusCode: validationError.statusCode || '500',
message: validationError.message
})
return renderErrorPage()
}
// ...If .validate() returned false
if (!isValid) {
// Render a 404 error page
return render404Page()
}
// If no Components found, returns 404
if (!Components.length) {
return render404Page()
}
// Call asyncData & fetch hooks on components matched by the route.
const asyncDatas = await Promise.all(Components.map((Component) => {
const promises = []
// Call asyncData(context)
if (Component.options.asyncData && typeof Component.options.asyncData === 'function') {
const promise = promisify(Component.options.asyncData, app.context)
promise.then((asyncDataResult) => {
ssrContext.asyncData[Component.cid] = asyncDataResult
applyAsyncData(Component)
return asyncDataResult
})
promises.push(promise)
} else {
promises.push(null)
}
// Call fetch(context)
if (Component.options.fetch && Component.options.fetch.length) {
promises.push(Component.options.fetch(app.context))
} else {
promises.push(null)
}
return Promise.all(promises)
}))
if (process.env.DEBUG && asyncDatas.length) console.debug('Data fetching ' + ssrContext.url + ': ' + (Date.now() - s) + 'ms')
// datas are the first row of each
ssrContext.nuxt.data = asyncDatas.map(r => r[0] || {})
// ...If there is a redirect or an error, stop the process
if (ssrContext.redirected) {
return noopApp()
}
if (ssrContext.nuxt.error) {
return renderErrorPage()
}
// Call beforeNuxtRender methods & add store state
await beforeRender()
return _app
}

178
shell/nuxt/store.js Normal file
View File

@ -0,0 +1,178 @@
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const VUEX_PROPERTIES = ['state', 'getters', 'actions', 'mutations']
let store = {};
(function updateModules () {
store = normalizeRoot(require('../store/index.js'), 'store/index.js')
// If store is an exported method = classic mode (deprecated)
if (typeof store === 'function') {
return console.warn('Classic mode for store/ is deprecated and will be removed in Nuxt 3.')
}
// Enforce store modules
store.modules = store.modules || {}
resolveStoreModules(require('../store/action-menu.js'), 'action-menu.js')
resolveStoreModules(require('../store/auth.js'), 'auth.js')
resolveStoreModules(require('../store/aws.js'), 'aws.js')
resolveStoreModules(require('../store/catalog.js'), 'catalog.js')
resolveStoreModules(require('../store/digitalocean.js'), 'digitalocean.js')
resolveStoreModules(require('../store/features.js'), 'features.js')
resolveStoreModules(require('../store/github.js'), 'github.js')
resolveStoreModules(require('../store/growl.js'), 'growl.js')
resolveStoreModules(require('../store/i18n.js'), 'i18n.js')
resolveStoreModules(require('../store/linode.js'), 'linode.js')
resolveStoreModules(require('../store/plugins.js'), 'plugins.js')
resolveStoreModules(require('../store/pnap.js'), 'pnap.js')
resolveStoreModules(require('../store/prefs.js'), 'prefs.js')
resolveStoreModules(require('../store/resource-fetch.js'), 'resource-fetch.js')
resolveStoreModules(require('../store/type-map.js'), 'type-map.js')
resolveStoreModules(require('../store/uiplugins.ts'), 'uiplugins.ts')
resolveStoreModules(require('../store/wm.js'), 'wm.js')
// If the environment supports hot reloading...
if (process.client && module.hot) {
// Whenever any Vuex module is updated...
module.hot.accept([
'../store/action-menu.js',
'../store/auth.js',
'../store/aws.js',
'../store/catalog.js',
'../store/digitalocean.js',
'../store/features.js',
'../store/github.js',
'../store/growl.js',
'../store/i18n.js',
'../store/index.js',
'../store/linode.js',
'../store/plugins.js',
'../store/pnap.js',
'../store/prefs.js',
'../store/resource-fetch.js',
'../store/type-map.js',
'../store/uiplugins.ts',
'../store/wm.js',
], () => {
// Update `root.modules` with the latest definitions.
updateModules()
// Trigger a hot update in the store.
window.$nuxt.$store.hotUpdate(store)
})
}
})()
// createStore
export const createStore = store instanceof Function ? store : () => {
return new Vuex.Store(Object.assign({
strict: (process.env.NODE_ENV !== 'production')
}, store))
}
function normalizeRoot (moduleData, filePath) {
moduleData = moduleData.default || moduleData
if (moduleData.commit) {
throw new Error(`[nuxt] ${filePath} should export a method that returns a Vuex instance.`)
}
if (typeof moduleData !== 'function') {
// Avoid TypeError: setting a property that has only a getter when overwriting top level keys
moduleData = Object.assign({}, moduleData)
}
return normalizeModule(moduleData, filePath)
}
function normalizeModule (moduleData, filePath) {
if (moduleData.state && typeof moduleData.state !== 'function') {
console.warn(`'state' should be a method that returns an object in ${filePath}`)
const state = Object.assign({}, moduleData.state)
// Avoid TypeError: setting a property that has only a getter when overwriting top level keys
moduleData = Object.assign({}, moduleData, { state: () => state })
}
return moduleData
}
function resolveStoreModules (moduleData, filename) {
moduleData = moduleData.default || moduleData
// Remove store src + extension (./foo/index.js -> foo/index)
const namespace = filename.replace(/\.(js|mjs|ts)$/, '')
const namespaces = namespace.split('/')
let moduleName = namespaces[namespaces.length - 1]
const filePath = `store/${filename}`
moduleData = moduleName === 'state'
? normalizeState(moduleData, filePath)
: normalizeModule(moduleData, filePath)
// If src is a known Vuex property
if (VUEX_PROPERTIES.includes(moduleName)) {
const property = moduleName
const propertyStoreModule = getStoreModule(store, namespaces, { isProperty: true })
// Replace state since it's a function
mergeProperty(propertyStoreModule, moduleData, property)
return
}
// If file is foo/index.js, it should be saved as foo
const isIndexModule = (moduleName === 'index')
if (isIndexModule) {
namespaces.pop()
moduleName = namespaces[namespaces.length - 1]
}
const storeModule = getStoreModule(store, namespaces)
for (const property of VUEX_PROPERTIES) {
mergeProperty(storeModule, moduleData[property], property)
}
if (moduleData.namespaced === false) {
delete storeModule.namespaced
}
}
function normalizeState (moduleData, filePath) {
if (typeof moduleData !== 'function') {
console.warn(`${filePath} should export a method that returns an object`)
const state = Object.assign({}, moduleData)
return () => state
}
return normalizeModule(moduleData, filePath)
}
function getStoreModule (storeModule, namespaces, { isProperty = false } = {}) {
// If ./mutations.js
if (!namespaces.length || (isProperty && namespaces.length === 1)) {
return storeModule
}
const namespace = namespaces.shift()
storeModule.modules[namespace] = storeModule.modules[namespace] || {}
storeModule.modules[namespace].namespaced = true
storeModule.modules[namespace].modules = storeModule.modules[namespace].modules || {}
return getStoreModule(storeModule.modules[namespace], namespaces, { isProperty })
}
function mergeProperty (storeModule, moduleData, property) {
if (!moduleData) {
return
}
if (property === 'state') {
storeModule.state = moduleData || storeModule.state
} else {
storeModule[property] = Object.assign({}, storeModule[property], moduleData)
}
}

630
shell/nuxt/utils.js Normal file
View File

@ -0,0 +1,630 @@
import Vue from 'vue'
import { isSamePath as _isSamePath, joinURL, normalizeURL, withQuery, withoutTrailingSlash } from 'ufo'
// window.{{globals.loadedCallback}} hook
// Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading)
if (process.client) {
window.onNuxtReadyCbs = []
window.onNuxtReady = (cb) => {
window.onNuxtReadyCbs.push(cb)
}
}
export function createGetCounter (counterObject, defaultKey = '') {
return function getCounter (id = defaultKey) {
if (counterObject[id] === undefined) {
counterObject[id] = 0
}
return counterObject[id]++
}
}
export function empty () {}
export function globalHandleError (error) {
if (Vue.config.errorHandler) {
Vue.config.errorHandler(error)
}
}
export function interopDefault (promise) {
return promise.then(m => m.default || m)
}
export function hasFetch(vm) {
return vm.$options && typeof vm.$options.fetch === 'function' && !vm.$options.fetch.length
}
export function purifyData(data) {
if (process.env.NODE_ENV === 'production') {
return data
}
return Object.entries(data).filter(
([key, value]) => {
const valid = !(value instanceof Function) && !(value instanceof Promise)
if (!valid) {
console.warn(`${key} is not able to be stringified. This will break in a production environment.`)
}
return valid
}
).reduce((obj, [key, value]) => {
obj[key] = value
return obj
}, {})
}
export function getChildrenComponentInstancesUsingFetch(vm, instances = []) {
const children = vm.$children || []
for (const child of children) {
if (child.$fetch) {
instances.push(child)
continue; // Don't get the children since it will reload the template
}
if (child.$children) {
getChildrenComponentInstancesUsingFetch(child, instances)
}
}
return instances
}
export function applyAsyncData (Component, asyncData) {
if (
// For SSR, we once all this function without second param to just apply asyncData
// Prevent doing this for each SSR request
!asyncData && Component.options.__hasNuxtData
) {
return
}
const ComponentData = Component.options._originDataFn || Component.options.data || function () { return {} }
Component.options._originDataFn = ComponentData
Component.options.data = function () {
const data = ComponentData.call(this, this)
if (this.$ssrContext) {
asyncData = this.$ssrContext.asyncData[Component.cid]
}
return { ...data, ...asyncData }
}
Component.options.__hasNuxtData = true
if (Component._Ctor && Component._Ctor.options) {
Component._Ctor.options.data = Component.options.data
}
}
export function sanitizeComponent (Component) {
// If Component already sanitized
if (Component.options && Component._Ctor === Component) {
return Component
}
if (!Component.options) {
Component = Vue.extend(Component) // fix issue #6
Component._Ctor = Component
} else {
Component._Ctor = Component
Component.extendOptions = Component.options
}
// If no component name defined, set file path as name, (also fixes #5703)
if (!Component.options.name && Component.options.__file) {
Component.options.name = Component.options.__file
}
return Component
}
export function getMatchedComponents (route, matches = false, prop = 'components') {
return Array.prototype.concat.apply([], route.matched.map((m, index) => {
return Object.keys(m[prop]).map((key) => {
matches && matches.push(index)
return m[prop][key]
})
}))
}
export function getMatchedComponentsInstances (route, matches = false) {
return getMatchedComponents(route, matches, 'instances')
}
export function flatMapComponents (route, fn) {
return Array.prototype.concat.apply([], route.matched.map((m, index) => {
return Object.keys(m.components).reduce((promises, key) => {
if (m.components[key]) {
promises.push(fn(m.components[key], m.instances[key], m, key, index))
} else {
delete m.components[key]
}
return promises
}, [])
}))
}
export function resolveRouteComponents (route, fn) {
return Promise.all(
flatMapComponents(route, async (Component, instance, match, key) => {
// If component is a function, resolve it
if (typeof Component === 'function' && !Component.options) {
try {
Component = await Component()
} catch (error) {
// Handle webpack chunk loading errors
// This may be due to a new deployment or a network problem
if (
error &&
error.name === 'ChunkLoadError' &&
typeof window !== 'undefined' &&
window.sessionStorage
) {
const timeNow = Date.now()
const previousReloadTime = parseInt(window.sessionStorage.getItem('nuxt-reload'))
// check for previous reload time not to reload infinitely
if (!previousReloadTime || previousReloadTime + 60000 < timeNow) {
window.sessionStorage.setItem('nuxt-reload', timeNow)
window.location.reload(true /* skip cache */)
}
}
throw error
}
}
match.components[key] = Component = sanitizeComponent(Component)
return typeof fn === 'function' ? fn(Component, instance, match, key) : Component
})
)
}
export async function getRouteData (route) {
if (!route) {
return
}
// Make sure the components are resolved (code-splitting)
await resolveRouteComponents(route)
// Send back a copy of route with meta based on Component definition
return {
...route,
meta: getMatchedComponents(route).map((Component, index) => {
return { ...Component.options.meta, ...(route.matched[index] || {}).meta }
})
}
}
export async function setContext (app, context) {
// If context not defined, create it
if (!app.context) {
app.context = {
isStatic: process.static,
isDev: true,
isHMR: false,
app,
store: app.store,
payload: context.payload,
error: context.error,
base: app.router.options.base,
env: {"commit":"head","version":"0.1.2","dev":true,"pl":1,"perfTest":false,"rancherEnv":"web","api":"http://localhost:8989"}
}
// Only set once
if (context.req) {
app.context.req = context.req
}
if (context.res) {
app.context.res = context.res
}
if (context.ssrContext) {
app.context.ssrContext = context.ssrContext
}
app.context.redirect = (status, path, query) => {
if (!status) {
return
}
app.context._redirected = true
// if only 1 or 2 arguments: redirect('/') or redirect('/', { foo: 'bar' })
let pathType = typeof path
if (typeof status !== 'number' && (pathType === 'undefined' || pathType === 'object')) {
query = path || {}
path = status
pathType = typeof path
status = 302
}
if (pathType === 'object') {
path = app.router.resolve(path).route.fullPath
}
// "/absolute/route", "./relative/route" or "../relative/route"
if (/(^[.]{1,2}\/)|(^\/(?!\/))/.test(path)) {
app.context.next({
path,
query,
status
})
} else {
path = withQuery(path, query)
if (process.server) {
app.context.next({
path,
status
})
}
if (process.client) {
// https://developer.mozilla.org/en-US/docs/Web/API/Location/replace
window.location.replace(path)
// Throw a redirect error
throw new Error('ERR_REDIRECT')
}
}
}
if (process.server) {
app.context.beforeNuxtRender = fn => context.beforeRenderFns.push(fn)
}
if (process.client) {
app.context.nuxtState = window.__NUXT__
}
}
// Dynamic keys
const [currentRouteData, fromRouteData] = await Promise.all([
getRouteData(context.route),
getRouteData(context.from)
])
if (context.route) {
app.context.route = currentRouteData
}
if (context.from) {
app.context.from = fromRouteData
}
app.context.next = context.next
app.context._redirected = false
app.context._errored = false
app.context.isHMR = Boolean(context.isHMR)
app.context.params = app.context.route.params || {}
app.context.query = app.context.route.query || {}
}
export function middlewareSeries (promises, appContext) {
if (!promises.length || appContext._redirected || appContext._errored) {
return Promise.resolve()
}
return promisify(promises[0], appContext)
.then(() => {
return middlewareSeries(promises.slice(1), appContext)
})
}
export function promisify (fn, context) {
let promise
if (fn.length === 2) {
console.warn('Callback-based asyncData, fetch or middleware calls are deprecated. ' +
'Please switch to promises or async/await syntax')
// fn(context, callback)
promise = new Promise((resolve) => {
fn(context, function (err, data) {
if (err) {
context.error(err)
}
data = data || {}
resolve(data)
})
})
} else {
promise = fn(context)
}
if (promise && promise instanceof Promise && typeof promise.then === 'function') {
return promise
}
return Promise.resolve(promise)
}
// Imported from vue-router
export function getLocation (base, mode) {
if (mode === 'hash') {
return window.location.hash.replace(/^#\//, '')
}
base = decodeURI(base).slice(0, -1) // consideration is base is normalized with trailing slash
let path = decodeURI(window.location.pathname)
if (base && path.startsWith(base)) {
path = path.slice(base.length)
}
const fullPath = (path || '/') + window.location.search + window.location.hash
return normalizeURL(fullPath)
}
// Imported from path-to-regexp
/**
* Compile a string to a template function for the path.
*
* @param {string} str
* @param {Object=} options
* @return {!function(Object=, Object=)}
*/
export function compile (str, options) {
return tokensToFunction(parse(str, options), options)
}
export function getQueryDiff (toQuery, fromQuery) {
const diff = {}
const queries = { ...toQuery, ...fromQuery }
for (const k in queries) {
if (String(toQuery[k]) !== String(fromQuery[k])) {
diff[k] = true
}
}
return diff
}
export function normalizeError (err) {
let message
if (!(err.message || typeof err === 'string')) {
try {
message = JSON.stringify(err, null, 2)
} catch (e) {
message = `[${err.constructor.name}]`
}
} else {
message = err.message || err
}
return {
...err,
message,
statusCode: (err.statusCode || err.status || (err.response && err.response.status) || 500)
}
}
/**
* The main path matching regexp utility.
*
* @type {RegExp}
*/
const PATH_REGEXP = new RegExp([
// Match escaped characters that would otherwise appear in future matches.
// This allows the user to escape special characters that won't transform.
'(\\\\.)',
// Match Express-style parameters and un-named parameters with a prefix
// and optional suffixes. Matches appear as:
//
// "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined]
// "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined]
// "/*" => ["/", undefined, undefined, undefined, undefined, "*"]
'([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))'
].join('|'), 'g')
/**
* Parse a string for the raw tokens.
*
* @param {string} str
* @param {Object=} options
* @return {!Array}
*/
function parse (str, options) {
const tokens = []
let key = 0
let index = 0
let path = ''
const defaultDelimiter = (options && options.delimiter) || '/'
let res
while ((res = PATH_REGEXP.exec(str)) != null) {
const m = res[0]
const escaped = res[1]
const offset = res.index
path += str.slice(index, offset)
index = offset + m.length
// Ignore already escaped sequences.
if (escaped) {
path += escaped[1]
continue
}
const next = str[index]
const prefix = res[2]
const name = res[3]
const capture = res[4]
const group = res[5]
const modifier = res[6]
const asterisk = res[7]
// Push the current path onto the tokens.
if (path) {
tokens.push(path)
path = ''
}
const partial = prefix != null && next != null && next !== prefix
const repeat = modifier === '+' || modifier === '*'
const optional = modifier === '?' || modifier === '*'
const delimiter = res[2] || defaultDelimiter
const pattern = capture || group
tokens.push({
name: name || key++,
prefix: prefix || '',
delimiter,
optional,
repeat,
partial,
asterisk: Boolean(asterisk),
pattern: pattern ? escapeGroup(pattern) : (asterisk ? '.*' : '[^' + escapeString(delimiter) + ']+?')
})
}
// Match any characters still remaining.
if (index < str.length) {
path += str.substr(index)
}
// If the path exists, push it onto the end.
if (path) {
tokens.push(path)
}
return tokens
}
/**
* Prettier encoding of URI path segments.
*
* @param {string}
* @return {string}
*/
function encodeURIComponentPretty (str, slashAllowed) {
const re = slashAllowed ? /[?#]/g : /[/?#]/g
return encodeURI(str).replace(re, (c) => {
return '%' + c.charCodeAt(0).toString(16).toUpperCase()
})
}
/**
* Encode the asterisk parameter. Similar to `pretty`, but allows slashes.
*
* @param {string}
* @return {string}
*/
function encodeAsterisk (str) {
return encodeURIComponentPretty(str, true)
}
/**
* Escape a regular expression string.
*
* @param {string} str
* @return {string}
*/
function escapeString (str) {
return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1')
}
/**
* Escape the capturing group by escaping special characters and meaning.
*
* @param {string} group
* @return {string}
*/
function escapeGroup (group) {
return group.replace(/([=!:$/()])/g, '\\$1')
}
/**
* Expose a method for transforming tokens into the path function.
*/
function tokensToFunction (tokens, options) {
// Compile all the tokens into regexps.
const matches = new Array(tokens.length)
// Compile all the patterns before compilation.
for (let i = 0; i < tokens.length; i++) {
if (typeof tokens[i] === 'object') {
matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$', flags(options))
}
}
return function (obj, opts) {
let path = ''
const data = obj || {}
const options = opts || {}
const encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i]
if (typeof token === 'string') {
path += token
continue
}
const value = data[token.name || 'pathMatch']
let segment
if (value == null) {
if (token.optional) {
// Prepend partial segment prefixes.
if (token.partial) {
path += token.prefix
}
continue
} else {
throw new TypeError('Expected "' + token.name + '" to be defined')
}
}
if (Array.isArray(value)) {
if (!token.repeat) {
throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`')
}
if (value.length === 0) {
if (token.optional) {
continue
} else {
throw new TypeError('Expected "' + token.name + '" to not be empty')
}
}
for (let j = 0; j < value.length; j++) {
segment = encode(value[j])
if (!matches[i].test(segment)) {
throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received `' + JSON.stringify(segment) + '`')
}
path += (j === 0 ? token.prefix : token.delimiter) + segment
}
continue
}
segment = token.asterisk ? encodeAsterisk(value) : encode(value)
if (!matches[i].test(segment)) {
throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"')
}
path += token.prefix + segment
}
return path
}
}
/**
* Get the flags for a regexp from the options.
*
* @param {Object} options
* @return {string}
*/
function flags (options) {
return options && options.sensitive ? '' : 'i'
}
export function addLifecycleHook(vm, hook, fn) {
if (!vm.$options[hook]) {
vm.$options[hook] = []
}
if (!vm.$options[hook].includes(fn)) {
vm.$options[hook].push(fn)
}
}
export const urlJoin = joinURL
export const stripTrailingSlash = withoutTrailingSlash
export const isSamePath = _isSamePath
export function setScrollRestoration (newVal) {
try {
window.history.scrollRestoration = newVal;
} catch(e) {}
}

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html {{ HTML_ATTRS }}>
<head {{ HEAD_ATTRS }}>
{{ HEAD }}
</head>
<body {{ BODY_ATTRS }}>
{{ APP }}
</body>
</html>

View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<title>Server error</title>
<meta charset="utf-8">
<meta content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" name=viewport>
<style>
.__nuxt-error-page{padding: 1rem;background:#f7f8fb;color:#47494e;text-align:center;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;font-family:sans-serif;font-weight:100!important;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased;position:absolute;top:0;left:0;right:0;bottom:0}.__nuxt-error-page .error{max-width:450px}.__nuxt-error-page .title{font-size:24px;font-size:1.5rem;margin-top:15px;color:#47494e;margin-bottom:8px}.__nuxt-error-page .description{color:#7f828b;line-height:21px;margin-bottom:10px}.__nuxt-error-page a{color:#7f828b!important;text-decoration:none}.__nuxt-error-page .logo{position:fixed;left:12px;bottom:12px}
</style>
</head>
<body>
<div class="__nuxt-error-page">
<div class="error">
<svg xmlns="http://www.w3.org/2000/svg" width="90" height="90" fill="#DBE1EC" viewBox="0 0 48 48"><path d="M22 30h4v4h-4zm0-16h4v12h-4zm1.99-10C12.94 4 4 12.95 4 24s8.94 20 19.99 20S44 35.05 44 24 35.04 4 23.99 4zM24 40c-8.84 0-16-7.16-16-16S15.16 8 24 8s16 7.16 16 16-7.16 16-16 16z"/></svg>
<div class="title">Server error</div>
<div class="description">{{ message }}</div>
</div>
<div class="logo">
<a href="https://nuxtjs.org" target="_blank" rel="noopener">Nuxt</a>
</div>
</div>
</body>
</html>

View File

@ -16,15 +16,11 @@
"clean": "./scripts/clean",
"lint": "./node_modules/.bin/eslint --max-warnings 0 --ext .ts,.js,.vue .",
"test": "./node_modules/.bin/nyc ava --serial --verbose",
"nuxt": "./node_modules/.bin/nuxt",
"dev": "./node_modules/.bin/nuxt dev",
"mem-dev": "node --max-old-space-size=8192 ./node_modules/.bin/nuxt dev",
"dev": "./node_modules/.bin/vue-cli-service dev",
"docker-dev": "docker run --rm --name dashboard-dev -p 8005:8005 -e API=$API -v $(pwd):/src -v dashboard_node:/src/node_modules rancher/dashboard:dev",
"build": "./node_modules/.bin/nuxt build --devtools",
"analyze": "./node_modules/.bin/nuxt build --analyze",
"start": "./node_modules/.bin/nuxt start",
"generate": "./node_modules/.bin/nuxt generate",
"dev-debug": "node --inspect ./node_modules/.bin/nuxt",
"build": "./node_modules/.bin/vue-cli-service build",
"analyze": "./node_modules/.bin/vue-cli-service build --report",
"start": "./node_modules/.bin/vue-cli-service start",
"cy:run": "cypress run",
"cy:open": "cypress open",
"e2e:pre": "NODE_ENV=dev yarn build",
@ -41,7 +37,6 @@
"@innologica/vue-dropdown-menu": "0.1.3",
"@novnc/novnc": "1.2.0",
"@nuxt/types": "2.14.6",
"@nuxt/typescript-build": "2.1.0",
"@nuxtjs/axios": "5.12.0",
"@nuxtjs/eslint-config-typescript": "6.0.1",
"@nuxtjs/eslint-module": "1.2.0",
@ -52,8 +47,6 @@
"@types/node": "16.4.3",
"@typescript-eslint/eslint-plugin": "4.33.0",
"@typescript-eslint/parser": "4.33.0",
"@vue/cli-plugin-babel": "4.5.15",
"@vue/cli-plugin-typescript": "4.5.15",
"@vue/cli-service": "4.5.15",
"@vue/test-utils": "1.2.1",
"@vue/vue2-jest": "27.0.0",
@ -157,5 +150,8 @@
".js",
".vue"
]
},
"devDependencies": {
"@vue/cli": "4.5.15"
}
}

View File

@ -495,7 +495,7 @@ export default {
}
.landscape {
background-image: url('~shell/assets/images/pl/login-landscape.svg');
background-image: url('~@shell/assets/images/pl/login-landscape.svg');
background-repeat: no-repeat;
background-size: cover;
background-position: center center;

View File

@ -354,7 +354,7 @@ const sharedActions = {
msg.selector = selector;
}
const worker = this.$workers[getters.storeName] || {};
const worker = this.$workers?.[getters.storeName] || {};
if (worker.mode === 'advanced') {
if ( force ) {

65
shell/public/index.html Normal file
View File

@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="shortcut icon" type="image/x-icon" href="/public/favicon.png">
<title>Rancher</title>
</head>
<body>
<div id="app">
<script>
(() => {
const isDark = document.cookie.includes('R_PCS=dark');
const color = isDark ? '#1b1c21' : '#FFF';
const style = document.createElement('style');
style.innerHTML = ':root { --loading-bg-color: ' + color + ';}';
document.getElementsByTagName('head')[0].prepend(style);
})();
</script>
<style>
.initial-load-spinner-container {
align-items: center;
background-color: var(--loading-bg-color);
display: flex;
justify-content: center;
height: 100vh;
left: 0;
position: absolute;
top: 0;
width: 100vw;
}
.initial-load-spinner {
animation: initial-load-animate 1s infinite linear;
background-color: var(--loading-bg-color);
box-sizing: border-box;
border: 5px solid #008ACF;
border-radius: 50%;
border-top-color: #00B2E2;
display: inline-block;
height: 80px;
margin: 0 auto;
width: 80px;
}
@keyframes initial-load-animate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(359deg);
}
}
</style>
<div class="initial-load-spinner-container">
<i class="initial-load-spinner"></i>
</div>
</div>
</body>
</html>

View File

@ -40,6 +40,7 @@ if [ -d "${BASE_DIR}/pkg/${1}" ]; then
echo " Package name: ${NAME}"
echo " Package version: ${VERSION}"
echo " Output formats: ${FORMATS}"
echo " Output directory: ${PKG_DIST}"
rm -rf ${PKG_DIST}
mkdir -p ${PKG_DIST}

View File

@ -17,16 +17,16 @@
"rootDir": ".",
"paths": {
"~/*": [
"./*"
"../*"
],
"@/*": [
"./*"
"../*"
],
"@shell/*": [
"./shell/*"
"../shell/*"
],
"@pkg/*": [
"./shell/pkg/*"
"../shell/pkg/*"
]
},
"typeRoots": [

24
shell/tsconfig.json Normal file
View File

@ -0,0 +1,24 @@
{
"extends": "./tsconfig.default.json",
"compilerOptions": {
"types": [
"@types/node",
"@types/jest",
"@nuxt/types"
]
},
"exclude": [
"node_modules",
".nuxt",
"dist",
"dist-pkg",
"cypress",
"shell/creators",
"shell/scripts",
"cypress",
"./cypress.config.ts",
"docusaurus",
"script/standalone",
"**/*.spec.ts"
]
}

669
shell/vue.config.ts Normal file
View File

@ -0,0 +1,669 @@
import fs from 'fs';
import path from 'path';
import serveStatic from 'serve-static';
import webpack from 'webpack';
import { STANDARD } from './config/private-label';
import { generateDynamicTypeImport } from './pkg/auto-import';
import CopyWebpackPlugin from 'copy-webpack-plugin';
import { createProxyMiddleware } from 'http-proxy-middleware';
const dev = (process.env.NODE_ENV !== 'production');
const devPorts = dev || process.env.DEV_PORTS === 'true';
// human readable version used on rancher dashboard about page
const dashboardVersion = process.env.DASHBOARD_VERSION;
const prime = process.env.PRIME;
const pl = process.env.PL || STANDARD;
const commit = process.env.COMMIT || 'head';
const perfTest = (process.env.PERF_TEST === 'true'); // Enable performance testing when in dev
const instrumentCode = (process.env.TEST_INSTRUMENT === 'true'); // Instrument code for code coverage in e2e tests
let api = process.env.API || 'http://localhost:8989';
if ( !api.startsWith('http') ) {
api = `https://${ api }`;
}
// ===============================================================================================
// Nuxt configuration
// ===============================================================================================
// Expose a function that can be used by an app to provide a nuxt configuration for building an application
// This takes the directory of the application as tehfirst argument so that we can derive folder locations
// from it, rather than from the location of this file
export default function(dir: any, _appConfig: any) {
// Paths to the shell folder when it is included as a node dependency
let SHELL = 'node_modules/@rancher/shell';
let SHELL_ABS = path.join(dir, 'node_modules/@rancher/shell');
let COMPONENTS_DIR = path.join(SHELL_ABS, 'rancher-components');
if (fs.existsSync(SHELL_ABS)) {
const stat = fs.lstatSync(SHELL_ABS);
// If @rancher/shell is a symlink, then use the components folder for it
if (stat.isSymbolicLink()) {
const REAL_SHELL_ABS = fs.realpathSync(SHELL_ABS); // In case the shell is being linked via 'yarn link'
COMPONENTS_DIR = path.join(REAL_SHELL_ABS, '..', 'pkg', 'rancher-components', 'src', 'components');
}
}
// If we have a local folder named 'shell' then use that rather than the one in node_modules
// This will be the case in the main dashboard repository.
if (fs.existsSync(path.join(dir, 'shell'))) {
SHELL = './shell';
SHELL_ABS = path.join(dir, 'shell');
COMPONENTS_DIR = path.join(dir, 'pkg', 'rancher-components', 'src', 'components');
}
const babelPlugins: string | (string | object)[] = [
// TODO: Browser support
// ['@babel/plugin-transform-modules-commonjs'],
['@babel/plugin-proposal-private-property-in-object', { loose: true }]
];
if (instrumentCode) {
babelPlugins.push('babel-plugin-istanbul');
console.warn('Instrumenting code for coverage'); // eslint-disable-line no-console
}
// ===============================================================================================
// Functions for the UI Pluginas
// ===============================================================================================
const appConfig = _appConfig || {};
const excludes = appConfig.excludes || [];
const autoLoad = appConfig.autoLoad || [];
const serverMiddleware = [];
const autoLoadPackages = [];
const watcherIgnores = [
/.shell/,
/dist-pkg/,
/scripts\/standalone/
];
autoLoad.forEach((pkg: any) => {
// Need the version number of each file
const pkgPackageFile = require(path.join(dir, 'pkg', pkg, 'package.json'));
const pkgRef = `${ pkg }-${ pkgPackageFile.version }`;
autoLoadPackages.push({
name: `app-autoload-${ pkgRef }`,
content: `/pkg/${ pkgRef }/${ pkgRef }.umd.min.js`
});
// Anything auto-loaded should also be excluded
if (!excludes.includes(pkg)) {
excludes.push(pkg);
}
});
// Find any UI packages in node_modules
const NM = path.join(dir, 'node_modules');
const pkg = require(path.join(dir, 'package.json'));
const nmPackages: any = {};
if (pkg && pkg.dependencies) {
Object.keys(pkg.dependencies).forEach((pkg) => {
const f = require(path.join(NM, pkg, 'package.json'));
// The package.json must have the 'rancher' property to mark it as a UI package
if (f.rancher) {
const id = `${ f.name }-${ f.version }`;
nmPackages[id] = f.main;
// Add server middleware to serve up the files for this UI package
serverMiddleware.push({
path: `/pkg/${ id }`,
handler: serveStatic(path.join(NM, pkg))
});
}
});
}
serverMiddleware.push({
path: '/uiplugins-catalog',
handler: (req: any, res: any, next: any) => {
const p = req.url.split('?');
try {
const proxy = createProxyMiddleware({
target: p[1],
pathRewrite: { '^.*': p[0] }
});
return proxy(req, res, next);
} catch (e) {
console.error(e); // eslint-disable-line no-console
}
}
});
function includePkg(name: any) {
if (name.startsWith('.') || name === 'node_modules') {
return false;
}
return !excludes || (excludes && !excludes.includes(name));
}
excludes.forEach((e: any) => {
watcherIgnores.push(new RegExp(`/pkg.${ e }`));
});
// For each package in the pkg folder that is being compiled into the application,
// Add in the code to automatically import the types from that package
// This imports models, edit, detail, list etc
// When built as a UI package, shell/pkg/vue.config.js does the same thing
const autoImportTypes: any = {};
const VirtualModulesPlugin = require('webpack-virtual-modules');
let reqs = '';
const pkgFolder = path.relative(dir, './pkg');
if (fs.existsSync(pkgFolder)) {
const items = fs.readdirSync(path.relative(dir, './pkg'));
// Ignore hidden folders
items.filter(name => !name.startsWith('.')).forEach((name) => {
const f = require(path.join(dir, 'pkg', name, 'package.json'));
// Package file must have rancher field to be a plugin
if (includePkg(name) && f.rancher) {
reqs += `$plugin.initPlugin('${ name }', require(\'~/pkg/${ name }\')); `;
}
// // Serve the code for the UI package in case its used for dynamic loading (but not if the same package was provided in node_modules)
// if (!nmPackages[name]) {
// const pkgPackageFile = require(path.join(dir, 'pkg', name, 'package.json'));
// const pkgRef = `${ name }-${ pkgPackageFile.version }`;
// serverMiddleware.push({ path: `/pkg/${ pkgRef }`, handler: serveStatic(`${ dir }/dist-pkg/${ pkgRef }`) });
// }
autoImportTypes[`node_modules/@rancher/auto-import/${ name }`] = generateDynamicTypeImport(`@pkg/${ name }`, path.join(dir, `pkg/${ name }`));
});
}
Object.keys(nmPackages).forEach((m) => {
reqs += `$plugin.loadAsync('${ m }', '/pkg/${ m }/${ nmPackages[m] }');`;
});
// Generate a virtual module '@rancher/dyanmic.js` which imports all of the packages that should be built into the application
// This is imported in 'shell/extensions/extension-loader.js` which ensures the all code for plugins to be included is imported in the application
const virtualModules = new VirtualModulesPlugin({ 'node_modules/@rancher/dynamic.js': `export default function ($plugin) { ${ reqs } };` });
const autoImport = new webpack.NormalModuleReplacementPlugin(/^@rancher\/auto-import$/, (resource: any) => {
const ctx = resource.context.split('/');
const pkg = ctx[ctx.length - 1];
resource.request = `@rancher/auto-import/${ pkg }`;
});
// @pkg imports must be resolved to the package that it importing them - this allows a package to use @pkg as an alis
// to the root of that particular package
const pkgImport = new webpack.NormalModuleReplacementPlugin(/^@pkg/, (resource: any) => {
const ctx = resource.context.split('/');
// Find 'pkg' folder in the contxt
const index = ctx.findIndex((s: any) => s === 'pkg');
if (index !== -1 && (index + 1) < ctx.length) {
const pkg = ctx[index + 1];
let p = path.resolve(dir, 'pkg', pkg, resource.request.substr(5));
if (resource.request.startsWith(`@pkg/${ pkg }`)) {
p = path.resolve(dir, 'pkg', resource.request.substr(5));
}
resource.request = p;
}
});
// Serve up the dist-pkg folder under /pkg
serverMiddleware.push({ path: `/pkg/`, handler: serveStatic(`${ dir }/dist-pkg/`) });
// Endpoint to download and unpack a tgz from the local verdaccio rgistry (dev)
serverMiddleware.push(path.resolve(dir, SHELL, 'server', 'verdaccio-middleware'));
// Add the standard dashboard server middleware after the middleware added to serve up UI packages
serverMiddleware.push(path.resolve(dir, SHELL, 'server', 'server-middleware'));
// ===============================================================================================
// Dashboard nuxt configuration
// ===============================================================================================
require('events').EventEmitter.defaultMaxListeners = 20;
require('dotenv').config();
let routerBasePath = '/';
let resourceBase = '';
let outputDir = 'dist';
if ( typeof process.env.ROUTER_BASE !== 'undefined' ) {
routerBasePath = process.env.ROUTER_BASE;
}
if ( typeof process.env.RESOURCE_BASE !== 'undefined' ) {
resourceBase = process.env.RESOURCE_BASE;
}
if ( typeof process.env.OUTPUT_DIR !== 'undefined' ) {
outputDir = process.env.OUTPUT_DIR;
}
if ( resourceBase && !resourceBase.endsWith('/') ) {
resourceBase += '/';
}
console.log(`Build: ${ dev ? 'Development' : 'Production' }`); // eslint-disable-line no-console
if ( !dev ) {
console.log(`Version: ${ dashboardVersion }`); // eslint-disable-line no-console
}
if ( !dev ) {
console.log(`Version: ${ dashboardVersion }`); // eslint-disable-line no-console
}
if ( resourceBase ) {
console.log(`Resource Base URL: ${ resourceBase }`); // eslint-disable-line no-console
}
if ( routerBasePath !== '/' ) {
console.log(`Router Base Path: ${ routerBasePath }`); // eslint-disable-line no-console
}
if ( pl !== STANDARD ) {
console.log(`PL: ${ pl }`); // eslint-disable-line no-console
}
const rancherEnv = process.env.RANCHER_ENV || 'web';
console.log(`API: '${ api }'. Env: '${ rancherEnv }'`); // eslint-disable-line no-console
const proxy = {
...appConfig.proxies,
'/k8s': proxyWsOpts(api), // Straight to a remote cluster (/k8s/clusters/<id>/)
'/pp': proxyWsOpts(api), // For (epinio) standalone API
'/api': proxyWsOpts(api), // Management k8s API
'/apis': proxyWsOpts(api), // Management k8s API
'/v1': proxyWsOpts(api), // Management Steve API
'/v3': proxyWsOpts(api), // Rancher API
'/v3-public': proxyOpts(api), // Rancher Unauthed API
'/api-ui': proxyOpts(api), // Browser API UI
'/meta': proxyMetaOpts(api), // Browser API UI
'/v1-*': proxyOpts(api), // SAML, KDM, etc
'/rancherversion': proxyPrimeOpts(api), // Rancher version endpoint
// These are for Ember embedding
'/c/*/edit': proxyOpts('https://127.0.0.1:8000'), // Can't proxy all of /c because that's used by Vue too
'/k/': proxyOpts('https://127.0.0.1:8000'),
'/g/': proxyOpts('https://127.0.0.1:8000'),
'/n/': proxyOpts('https://127.0.0.1:8000'),
'/p/': proxyOpts('https://127.0.0.1:8000'),
'/assets': proxyOpts('https://127.0.0.1:8000'),
'/translations': proxyOpts('https://127.0.0.1:8000'),
'/engines-dist': proxyOpts('https://127.0.0.1:8000'),
};
const config = {
// Vue server
devServer: {
https: (devPorts ? {
key: fs.readFileSync(path.resolve(__dirname, 'server/server.key')),
cert: fs.readFileSync(path.resolve(__dirname, 'server/server.crt'))
} : null),
port: (devPorts ? 8005 : 80),
host: '0.0.0.0',
public: `https://0.0.0.0:${ devPorts ? 8005 : 80 }`,
before(app: any, server: any) {
const proxies: any = {};
Object.keys(proxy).forEach((p) => {
const px = createProxyMiddleware({
...proxy[p],
ws: false // We will handle the web socket upgrade
});
proxies[p] = px;
app.use(p, px);
});
server.websocketProxies.push({
upgrade(req: any, socket: any, head:any) {
if (req.url.startsWith('/v1')) {
return proxies['/v1'].upgrade(req, socket, head);
} else if (req.url.startsWith('/v3')) {
return proxies['/v3'].upgrade(req, socket, head);
} else if (req.url.startsWith('/k8s/')) {
return proxies['/k8s'].upgrade(req, socket, head);
} else {
console.log(`Unknown Web socket upgrade request for ${ req.url }`); // eslint-disable-line no-console
}
}
});
},
},
css: {
loaderOptions: {
sass: {
// This is effectively added to the beginning of each style that's imported or included in a vue file. We may want to look into including these in app.scss
additionalData: `
@use 'sass:math';
@import "~shell/assets/styles/base/_variables.scss";
@import "~shell/assets/styles/base/_functions.scss";
@import "~shell/assets/styles/base/_mixins.scss";
`
}
}
},
outputDir,
pages: {
index: {
entry: path.join(SHELL_ABS, '/nuxt/client.js'),
template: path.join(SHELL_ABS, '/public/index.html')
}
},
configureWebpack(config: any) {
config.resolve.alias['~'] = dir;
config.resolve.alias['@'] = dir;
config.resolve.alias['~assets'] = path.join(__dirname, 'assets');
config.resolve.alias['~shell'] = SHELL_ABS;
config.resolve.alias['@shell'] = SHELL_ABS;
config.resolve.alias['@pkg'] = path.join(dir, 'pkg');
config.resolve.alias['./node_modules'] = path.join(dir, 'node_modules');
config.resolve.alias['@components'] = COMPONENTS_DIR;
config.resolve.modules.push(__dirname);
config.plugins.push(virtualModules);
config.plugins.push(autoImport);
config.plugins.push(new VirtualModulesPlugin(autoImportTypes));
config.plugins.push(pkgImport);
// DefinePlugin does string replacement within our code. We may want to consider replacing it with something else. In code we'll see something like
// process.env.commit even though process and env aren't even defined objects. This could cause people to be mislead.
config.plugins.push(new webpack.DefinePlugin({
'process.client': JSON.stringify(true),
'process.env.commit': JSON.stringify(commit),
'process.env.version': JSON.stringify(dashboardVersion),
'process.env.dev': JSON.stringify(dev),
'process.env.pl': JSON.stringify(pl),
'process.env.perfTest': JSON.stringify(perfTest),
'process.env.rancherEnv': JSON.stringify(rancherEnv),
'process.env.harvesterPkgUrl': JSON.stringify(process.env.HARVESTER_PKG_URL),
'process.env.api': JSON.stringify(api),
// This is a replacement of the nuxt publicRuntimeConfig
'nuxt.publicRuntimeConfig': JSON.stringify({
rancherEnv,
dashboardVersion
}),
}));
// The static assets need to be in the built public folder in order to get served (primarily the favicon for now)
config.plugins.push(new CopyWebpackPlugin([{ from: path.join(SHELL_ABS, 'static'), to: 'public' }]));
config.resolve.extensions.push(...['.tsx', '.ts', '.js', '.vue', '.scss']);
config.watchOptions = config.watchOptions || {};
config.watchOptions.ignored = watcherIgnores;
if (dev) {
config.devtool = 'cheap-module-source-map';
} else {
config.devtool = 'source-map';
}
if (resourceBase) {
config.output.publicPath = resourceBase;
}
config.resolve.symlinks = false;
// Ensure we process files in the @rancher/shell folder
config.module.rules.forEach((r: any) => {
if ('test.js'.match(r.test)) {
if (r.exclude) {
const orig = r.exclude;
r.exclude = function(modulePath: string) {
if (modulePath.indexOf(SHELL_ABS) === 0) {
return false;
}
return orig(modulePath);
};
}
}
});
// Instrument code for tests
const babelPlugins: (string | ([] | Object)[])[] = [
// TODO: Browser support
// ['@babel/plugin-transform-modules-commonjs'],
['@babel/plugin-proposal-private-property-in-object', { loose: true }],
['@babel/plugin-proposal-class-properties', { loose: true }]
];
if (instrumentCode) {
babelPlugins.push('babel-plugin-istanbul');
console.warn('Instrumenting code for coverage'); // eslint-disable-line no-console
}
const loaders = [
// Ensure there is a fallback for browsers that don't support web workers
{
test: /web-worker.[a-z-]+.js/i,
loader: 'worker-loader',
options: { inline: 'fallback' },
},
// Handler for csv files (e.g. ec2 instance data)
{
test: /\.csv$/i,
loader: 'csv-loader',
options: {
dynamicTyping: true,
header: true,
skipEmptyLines: true
},
},
// Handler for yaml files (used for i18n files, for example)
{
test: /\.ya?ml$/i,
loader: 'js-yaml-loader',
options: { name: '[path][name].[ext]' },
},
{
test: /\.m?[tj]sx?$/,
// This excludes no modules except for node_modules/@rancher/... so that plugins can properly compile
// when referencing @rancher/shell
exclude: /node_modules\/(?!(@rancher)\/).*/,
use: [
{
loader: 'cache-loader',
options: {
cacheDirectory: 'node_modules/.cache/babel-loader',
cacheIdentifier: 'e93f32da'
}
},
{
loader: 'babel-loader',
options: {
presets: [
[
require.resolve('@nuxt/babel-preset-app'),
{
corejs: { version: 3 },
targets: { browsers: ['last 2 versions'] },
modern: true
}
],
'@babel/preset-typescript',
],
plugins: babelPlugins
}
}
]
},
{
test: /\.tsx?$/,
use: [
{
loader: 'cache-loader',
options: {
cacheDirectory: 'node_modules/.cache/ts-loader',
cacheIdentifier: '3596741e'
}
},
{
loader: 'ts-loader',
options: {
transpileOnly: true,
happyPackMode: false,
appendTsxSuffixTo: [
'\\.vue$'
],
configFile: path.join(SHELL_ABS, 'tsconfig.json')
}
}
]
},
// Prevent warning in log with the md files in the content folder
{
test: /\.md$/,
use: [
{
loader: 'url-loader',
options: {
name: '[path][name].[ext]',
limit: 1,
esModule: false
},
}
]
},
// Prevent warning in log with the md files in the content folder
{
test: /\.md$/,
use: [
{
loader: 'frontmatter-markdown-loader',
options: { mode: ['body'] }
}
]
}
];
config.module.rules.push(...loaders);
},
};
return config;
}
// ===============================================================================================
// Functions for the request proxying used in dev
// ===============================================================================================
function proxyMetaOpts(target: any) {
return {
target,
followRedirects: true,
secure: !dev,
onProxyReq,
onProxyReqWs,
onError,
onProxyRes,
};
}
function proxyOpts(target: any) {
return {
target,
secure: !devPorts,
onProxyReq,
onProxyReqWs,
onError,
onProxyRes
};
}
// Intercept the /rancherversion API call wnad modify the 'RancherPrime' value
// if configured to do so by the environment variable PRIME
function proxyPrimeOpts(target: any) {
const opts = proxyOpts(target);
// Don't intercept if the PRIME environment variable is not set
if (!prime?.length) {
return opts;
}
opts.onProxyRes = (proxyRes, req, res) => {
const _end = res.end;
let body = '';
proxyRes.on( 'data', (data: any) => {
data = data.toString('utf-8');
body += data;
});
res.write = () => {};
res.end = () => {
let output = body;
try {
const out = JSON.parse(body);
out.RancherPrime = prime;
output = JSON.stringify(out);
} catch (err) {}
res.setHeader('content-length', output.length );
res.setHeader('content-type', 'application/json' );
res.setHeader('transfer-encoding', '');
res.setHeader('cache-control', 'no-cache');
res.writeHead(proxyRes.statusCode);
_end.apply(res, [output]);
};
};
return opts;
}
function onProxyRes(proxyRes: any, req: any, res: any) {
if (devPorts) {
proxyRes.headers['X-Frame-Options'] = 'ALLOWALL';
}
}
function proxyWsOpts(target: any) {
return {
...proxyOpts(target),
ws: true,
changeOrigin: true,
};
}
function onProxyReq(proxyReq: any, req: any) {
if (!(proxyReq._currentRequest && proxyReq._currentRequest._headerSent)) {
proxyReq.setHeader('x-api-host', req.headers['host']);
proxyReq.setHeader('x-forwarded-proto', 'https');
}
}
function onProxyReqWs(proxyReq: any, req: any, socket: any, options: any, head: any) {
req.headers.origin = options.target.href;
proxyReq.setHeader('origin', options.target.href);
proxyReq.setHeader('x-api-host', req.headers['host']);
proxyReq.setHeader('x-forwarded-proto', 'https');
// console.log(proxyReq.getHeaders());
socket.on('error', (err: any) => {
console.error('Proxy WS Error:', err); // eslint-disable-line no-console
});
}
function onError(err: any, req: any, res: any) {
res.statusCode = 598;
console.error('Proxy Error:', err); // eslint-disable-line no-console
res.write(JSON.stringify(err));
}

View File

@ -1,20 +1,3 @@
{
"extends": "./tsconfig.default.json",
"compilerOptions": {
"types": ["@types/node", "@types/jest", "@nuxt/types"]
},
"exclude": [
"node_modules",
".nuxt",
"dist",
"dist-pkg",
"cypress",
"shell/creators",
"shell/scripts",
"cypress",
"./cypress.config.ts",
"docusaurus",
"script/standalone",
"**/*.spec.ts"
]
}
"extends": "./shell/tsconfig.json",
}

7
vue.config.js Normal file
View File

@ -0,0 +1,7 @@
require('ts-node').register({
project: './tsconfig.json',
compilerOptions: { module: 'commonjs' },
logError: true
});
module.exports = require('./vue.config.ts').default;

View File

@ -1,4 +1,5 @@
import config from './shell/nuxt.config';
import config from './shell/vue.config';
// Excludes the following plugins if there's no .env file.
let defaultExcludes = 'epinio, rancher-components, harvester';

231
yarn.lock
View File

@ -1001,7 +1001,12 @@
dependencies:
"@babel/highlight" "^7.18.6"
"@babel/compat-data@^7.14.0", "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.19.3":
"@babel/compat-data@^7.14.0", "@babel/compat-data@^7.19.4":
version "7.19.4"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.4.tgz#95c86de137bf0317f3a570e1b6e996b427299747"
integrity sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==
"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.18.8", "@babel/compat-data@^7.19.3":
version "7.19.3"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.3.tgz#707b939793f867f5a73b2666e6d9a3396eb03151"
integrity sha512-prBHMK4JYYK+wDjJF1q99KK4JLL+egWS4nmNqdlMUgCExMZ+iZW0hGhyC3VEbsPjvaN0TBhW//VIFwBrk8sEiw==
@ -1217,6 +1222,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56"
integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==
"@babel/helper-string-parser@^7.19.4":
version "7.19.4"
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63"
integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==
"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1":
version "7.19.1"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
@ -1373,6 +1383,17 @@
"@babel/plugin-syntax-object-rest-spread" "^7.8.3"
"@babel/plugin-transform-parameters" "^7.18.8"
"@babel/plugin-proposal-object-rest-spread@^7.19.4":
version "7.19.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.19.4.tgz#a8fc86e8180ff57290c91a75d83fe658189b642d"
integrity sha512-wHmj6LDxVDnL+3WhXteUBaoM1aVILZODAUjg11kHqG4cOlfgMQGxw6aCgvrXrmaJR3Bn14oZhImyCPZzRpC93Q==
dependencies:
"@babel/compat-data" "^7.19.4"
"@babel/helper-compilation-targets" "^7.19.3"
"@babel/helper-plugin-utils" "^7.19.0"
"@babel/plugin-syntax-object-rest-spread" "^7.8.3"
"@babel/plugin-transform-parameters" "^7.18.8"
"@babel/plugin-proposal-optional-catch-binding@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb"
@ -1605,6 +1626,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.18.9"
"@babel/plugin-transform-block-scoping@^7.19.4":
version "7.19.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.19.4.tgz#315d70f68ce64426db379a3d830e7ac30be02e9b"
integrity sha512-934S2VLLlt2hRJwPf4MczaOr4hYF0z+VKPwqTNxyKX7NthTiPfhuKFWQZHXRM0vh/wo/VyXB3s4bZUNA08l+tQ==
dependencies:
"@babel/helper-plugin-utils" "^7.19.0"
"@babel/plugin-transform-classes@^7.19.0":
version "7.19.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz#0e61ec257fba409c41372175e7c1e606dc79bb20"
@ -1634,6 +1662,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.18.9"
"@babel/plugin-transform-destructuring@^7.19.4":
version "7.19.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.19.4.tgz#46890722687b9b89e1369ad0bd8dc6c5a3b4319d"
integrity sha512-t0j0Hgidqf0aM86dF8U+vXYReUgJnlv4bZLsyoPnwZNrGY+7/38o8YjaELrvHeVfTZao15kjR0PVv0nju2iduA==
dependencies:
"@babel/helper-plugin-utils" "^7.19.0"
"@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8"
@ -1849,7 +1884,7 @@
"@babel/helper-create-regexp-features-plugin" "^7.18.6"
"@babel/helper-plugin-utils" "^7.18.6"
"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.14.1", "@babel/preset-env@^7.4.4":
"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.4.4":
version "7.19.3"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.19.3.tgz#52cd19abaecb3f176a4ff9cc5e15b7bf06bec754"
integrity sha512-ziye1OTc9dGFOAXSWKUqQblYHNlBOaDl8wzqf2iKXJAltYiR3hKHUKmkt+S9PppW7RQpq4fFCrwwpIDj/f5P4w==
@ -1930,6 +1965,87 @@
core-js-compat "^3.25.1"
semver "^6.3.0"
"@babel/preset-env@^7.14.1":
version "7.19.4"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.19.4.tgz#4c91ce2e1f994f717efb4237891c3ad2d808c94b"
integrity sha512-5QVOTXUdqTCjQuh2GGtdd7YEhoRXBMVGROAtsBeLGIbIz3obCBIfRMT1I3ZKkMgNzwkyCkftDXSSkHxnfVf4qg==
dependencies:
"@babel/compat-data" "^7.19.4"
"@babel/helper-compilation-targets" "^7.19.3"
"@babel/helper-plugin-utils" "^7.19.0"
"@babel/helper-validator-option" "^7.18.6"
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6"
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9"
"@babel/plugin-proposal-async-generator-functions" "^7.19.1"
"@babel/plugin-proposal-class-properties" "^7.18.6"
"@babel/plugin-proposal-class-static-block" "^7.18.6"
"@babel/plugin-proposal-dynamic-import" "^7.18.6"
"@babel/plugin-proposal-export-namespace-from" "^7.18.9"
"@babel/plugin-proposal-json-strings" "^7.18.6"
"@babel/plugin-proposal-logical-assignment-operators" "^7.18.9"
"@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6"
"@babel/plugin-proposal-numeric-separator" "^7.18.6"
"@babel/plugin-proposal-object-rest-spread" "^7.19.4"
"@babel/plugin-proposal-optional-catch-binding" "^7.18.6"
"@babel/plugin-proposal-optional-chaining" "^7.18.9"
"@babel/plugin-proposal-private-methods" "^7.18.6"
"@babel/plugin-proposal-private-property-in-object" "^7.18.6"
"@babel/plugin-proposal-unicode-property-regex" "^7.18.6"
"@babel/plugin-syntax-async-generators" "^7.8.4"
"@babel/plugin-syntax-class-properties" "^7.12.13"
"@babel/plugin-syntax-class-static-block" "^7.14.5"
"@babel/plugin-syntax-dynamic-import" "^7.8.3"
"@babel/plugin-syntax-export-namespace-from" "^7.8.3"
"@babel/plugin-syntax-import-assertions" "^7.18.6"
"@babel/plugin-syntax-json-strings" "^7.8.3"
"@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
"@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
"@babel/plugin-syntax-numeric-separator" "^7.10.4"
"@babel/plugin-syntax-object-rest-spread" "^7.8.3"
"@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
"@babel/plugin-syntax-optional-chaining" "^7.8.3"
"@babel/plugin-syntax-private-property-in-object" "^7.14.5"
"@babel/plugin-syntax-top-level-await" "^7.14.5"
"@babel/plugin-transform-arrow-functions" "^7.18.6"
"@babel/plugin-transform-async-to-generator" "^7.18.6"
"@babel/plugin-transform-block-scoped-functions" "^7.18.6"
"@babel/plugin-transform-block-scoping" "^7.19.4"
"@babel/plugin-transform-classes" "^7.19.0"
"@babel/plugin-transform-computed-properties" "^7.18.9"
"@babel/plugin-transform-destructuring" "^7.19.4"
"@babel/plugin-transform-dotall-regex" "^7.18.6"
"@babel/plugin-transform-duplicate-keys" "^7.18.9"
"@babel/plugin-transform-exponentiation-operator" "^7.18.6"
"@babel/plugin-transform-for-of" "^7.18.8"
"@babel/plugin-transform-function-name" "^7.18.9"
"@babel/plugin-transform-literals" "^7.18.9"
"@babel/plugin-transform-member-expression-literals" "^7.18.6"
"@babel/plugin-transform-modules-amd" "^7.18.6"
"@babel/plugin-transform-modules-commonjs" "^7.18.6"
"@babel/plugin-transform-modules-systemjs" "^7.19.0"
"@babel/plugin-transform-modules-umd" "^7.18.6"
"@babel/plugin-transform-named-capturing-groups-regex" "^7.19.1"
"@babel/plugin-transform-new-target" "^7.18.6"
"@babel/plugin-transform-object-super" "^7.18.6"
"@babel/plugin-transform-parameters" "^7.18.8"
"@babel/plugin-transform-property-literals" "^7.18.6"
"@babel/plugin-transform-regenerator" "^7.18.6"
"@babel/plugin-transform-reserved-words" "^7.18.6"
"@babel/plugin-transform-shorthand-properties" "^7.18.6"
"@babel/plugin-transform-spread" "^7.19.0"
"@babel/plugin-transform-sticky-regex" "^7.18.6"
"@babel/plugin-transform-template-literals" "^7.18.9"
"@babel/plugin-transform-typeof-symbol" "^7.18.9"
"@babel/plugin-transform-unicode-escapes" "^7.18.10"
"@babel/plugin-transform-unicode-regex" "^7.18.6"
"@babel/preset-modules" "^0.1.5"
"@babel/types" "^7.19.4"
babel-plugin-polyfill-corejs2 "^0.3.3"
babel-plugin-polyfill-corejs3 "^0.6.0"
babel-plugin-polyfill-regenerator "^0.4.1"
core-js-compat "^3.25.1"
semver "^6.3.0"
"@babel/preset-modules@^0.1.5":
version "0.1.5"
resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9"
@ -1950,13 +2066,20 @@
"@babel/helper-validator-option" "^7.16.7"
"@babel/plugin-transform-typescript" "^7.16.7"
"@babel/runtime@^7.11.0", "@babel/runtime@^7.14.0", "@babel/runtime@^7.15.4", "@babel/runtime@^7.8.4":
"@babel/runtime@^7.11.0", "@babel/runtime@^7.15.4", "@babel/runtime@^7.8.4":
version "7.19.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259"
integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.14.0":
version "7.19.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.4.tgz#a42f814502ee467d55b38dd1c256f53a7b885c78"
integrity sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.3.3":
version "7.18.10"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71"
@ -1991,6 +2114,15 @@
"@babel/helper-validator-identifier" "^7.19.1"
to-fast-properties "^2.0.0"
"@babel/types@^7.19.4":
version "7.19.4"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.4.tgz#0dd5c91c573a202d600490a35b33246fed8a41c7"
integrity sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==
dependencies:
"@babel/helper-string-parser" "^7.19.4"
"@babel/helper-validator-identifier" "^7.19.1"
to-fast-properties "^2.0.0"
"@bcoe/v8-coverage@^0.2.3":
version "0.2.3"
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
@ -3122,6 +3254,15 @@
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803"
integrity sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==
"@types/copy-webpack-plugin@^5.0.3":
version "5.0.3"
resolved "https://registry.yarnpkg.com/@types/copy-webpack-plugin/-/copy-webpack-plugin-5.0.3.tgz#9c7ea433ea0abcd0ad1b65a6d11a92c4ad0c691c"
integrity sha512-+Iv2lqU4lvAQdXMtQYjT2VuhPcWP+UWdQXfTiz2yfypo2XzEv8WggQcFfK481x1iav3W6FV0bzXBXnlq7+ukhg==
dependencies:
"@types/minimatch" "*"
"@types/node" "*"
"@types/webpack" "^4"
"@types/eslint-scope@^3.7.3":
version "3.7.4"
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16"
@ -3870,6 +4011,15 @@
postcss "^8.4.14"
source-map "^0.6.1"
"@vue/compiler-sfc@2.7.11":
version "2.7.11"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-2.7.11.tgz#3b8a60b4145f615a5da023492ee34cc3bb2b545b"
integrity sha512-Cf8zvrZWjROgd8yPL8Tc+O3q/Y8ZGM0Y+8blrAvj1RQsVouzUY0oHcx8BA7Hybhb90JRnzeApFrlQGZRUdYpRw==
dependencies:
"@babel/parser" "^7.18.4"
postcss "^8.4.14"
source-map "^0.6.1"
"@vue/component-compiler-utils@^2.3.1":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-2.6.0.tgz#aa46d2a6f7647440b0b8932434d22f12371e543b"
@ -5487,11 +5637,16 @@ caniuse-api@^3.0.0:
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001228, caniuse-lite@^1.0.30001400:
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001400:
version "1.0.30001412"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001412.tgz#30f67d55a865da43e0aeec003f073ea8764d5d7c"
integrity sha512-+TeEIee1gS5bYOiuf+PS/kp2mrXic37Hl66VY6EAfxasIk5fELTktK2oOezYed12H8w7jt3s512PpulQidPjwA==
caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001228:
version "1.0.30001418"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz#5f459215192a024c99e3e3a53aac310fc7cf24e6"
integrity sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==
case-sensitive-paths-webpack-plugin@^2.3.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4"
@ -5617,7 +5772,12 @@ ci-info@^2.0.0:
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
ci-info@^3.1.1, ci-info@^3.2.0:
ci-info@^3.1.1:
version "3.5.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.5.0.tgz#bfac2a29263de4c829d806b1ab478e35091e171f"
integrity sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==
ci-info@^3.2.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.4.0.tgz#b28484fd436cbc267900364f096c9dc185efb251"
integrity sha512-t5QdPT5jq3o262DOQ8zA6E1tlH2upmUc4Hlvrbx1pGYJuiiHl7O7rvVNI+l8HTVhd/q3Qc9vqimkNk5yiXsAug==
@ -6142,7 +6302,14 @@ copy-webpack-plugin@^5.1.1:
serialize-javascript "^4.0.0"
webpack-log "^2.0.0"
core-js-compat@^3.12.1, core-js-compat@^3.25.1, core-js-compat@^3.6.5:
core-js-compat@^3.12.1:
version "3.25.5"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.25.5.tgz#0016e8158c904f7b059486639e6e82116eafa7d9"
integrity sha512-ovcyhs2DEBUIE0MGEKHP4olCUW/XYte3Vroyxuh38rD1wAO4dHohsovUC4eAOuzFxE6b+RXvBU3UZ9o0YhUTkA==
dependencies:
browserslist "^4.21.4"
core-js-compat@^3.25.1, core-js-compat@^3.6.5:
version "3.25.3"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.25.3.tgz#d6a442a03f4eade4555d4e640e6a06151dd95d38"
integrity sha512-xVtYpJQ5grszDHEUU9O7XbjjcZ0ccX3LgQsyqSvTnjX97ZqEgn9F5srmrwwwMtbKzDllyFPL+O+2OFMl1lU4TQ==
@ -16267,7 +16434,7 @@ terser@^4.1.2, terser@^4.3.9, terser@^4.6.3:
source-map "~0.6.1"
source-map-support "~0.5.12"
terser@^5.10.0, terser@^5.14.1, terser@^5.3.4:
terser@^5.10.0, terser@^5.14.1:
version "5.15.0"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.15.0.tgz#e16967894eeba6e1091509ec83f0c60e179f2425"
integrity sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==
@ -16277,6 +16444,16 @@ terser@^5.10.0, terser@^5.14.1, terser@^5.3.4:
commander "^2.20.0"
source-map-support "~0.5.20"
terser@^5.3.4:
version "5.15.1"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.15.1.tgz#8561af6e0fd6d839669c73b92bdd5777d870ed6c"
integrity sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==
dependencies:
"@jridgewell/source-map" "^0.3.2"
acorn "^8.5.0"
commander "^2.20.0"
source-map-support "~0.5.20"
test-exclude@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"
@ -16711,7 +16888,7 @@ uc.micro@^1.0.1, uc.micro@^1.0.5:
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
ufo@^0.7.4:
ufo@0.7.11, ufo@^0.7.4:
version "0.7.11"
resolved "https://registry.yarnpkg.com/ufo/-/ufo-0.7.11.tgz#17defad497981290383c5d26357773431fdbadcb"
integrity sha512-IT3q0lPvtkqQ8toHQN/BkOi4VIqoqheqM1FnkNWT9y0G8B3xJhwnoKBu5OHx8zHDOvveQzfKuFowJ0VSARiIDg==
@ -16725,9 +16902,9 @@ uglify-js@3.4.x:
source-map "~0.6.1"
uglify-js@^3.5.1:
version "3.17.2"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.2.tgz#f55f668b9a64b213977ae688703b6bbb7ca861c6"
integrity sha512-bbxglRjsGQMchfvXZNusUcYgiB9Hx2K4AHYXQy2DITZ9Rd+JzhX7+hoocE5Winr7z2oHvPsekkBwXtigvxevXg==
version "3.17.3"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.3.tgz#f0feedf019c4510f164099e8d7e72ff2d7304377"
integrity sha512-JmMFDME3iufZnBpyKL+uS78LRiC+mK55zWfM5f/pWBJfpOttXAqYfdDGRukYhJuyRinvPVAtUhvy7rlDybNtFg==
un-eval@^1.2.0:
version "1.2.0"
@ -16749,7 +16926,7 @@ undefsafe@^2.0.2:
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c"
integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==
unfetch@^4.2.0:
unfetch@4.2.0, unfetch@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be"
integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==
@ -17068,7 +17245,7 @@ vm-browserify@^1.0.1:
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
vue-client-only@^2.0.0:
vue-client-only@2.1.0, vue-client-only@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/vue-client-only/-/vue-client-only-2.1.0.tgz#1a67a47b8ecacfa86d75830173fffee3bf8a4ee3"
integrity sha512-vKl1skEKn8EK9f8P2ZzhRnuaRHLHrlt1sbRmazlvsx6EiC3A8oWF8YCBrMJzoN+W3OnElwIGbVjsx6/xelY1AA==
@ -17131,14 +17308,14 @@ vue-loader@^15.9.2, vue-loader@^15.9.7:
vue-hot-reload-api "^2.3.0"
vue-style-loader "^4.1.0"
vue-meta@^2.4.0:
vue-meta@2.4.0, vue-meta@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/vue-meta/-/vue-meta-2.4.0.tgz#a419fb4b4135ce965dab32ec641d1989c2ee4845"
integrity sha512-XEeZUmlVeODclAjCNpWDnjgw+t3WA6gdzs6ENoIAgwO1J1d5p1tezDhtteLUFwcaQaTtayRrsx7GL6oXp/m2Jw==
dependencies:
deepmerge "^4.2.2"
vue-no-ssr@^1.1.1:
vue-no-ssr@1.1.1, vue-no-ssr@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/vue-no-ssr/-/vue-no-ssr-1.1.1.tgz#875f3be6fb0ae41568a837f3ac1a80eaa137b998"
integrity sha512-ZMjqRpWabMPqPc7gIrG0Nw6vRf1+itwf0Itft7LbMXs2g3Zs/NFmevjZGN1x7K3Q95GmIjWbQZTVerxiBxI+0g==
@ -17148,7 +17325,7 @@ vue-resize@0.4.5, vue-resize@^0.4.5:
resolved "https://registry.yarnpkg.com/vue-resize/-/vue-resize-0.4.5.tgz#4777a23042e3c05620d9cbda01c0b3cc5e32dcea"
integrity sha512-bhP7MlgJQ8TIkZJXAfDf78uJO+mEI3CaLABLjv0WNzr4CcGRGPIAItyWYnP6LsPA4Oq0WE+suidNs6dgpO4RHg==
vue-router@^3.5.1:
vue-router@3.6.5, vue-router@^3.5.1:
version "3.6.5"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.6.5.tgz#95847d52b9a7e3f1361cb605c8e6441f202afad8"
integrity sha512-VYXZQLtjuvKxxcshuRAwjHnciqZVoXAjTjcqBTz4rKc8qih9g9pI3hbDjmqXaHdgL3v8pV6P8Z335XvHzESxLQ==
@ -17173,9 +17350,9 @@ vue-server-renderer@2.6.14:
source-map "0.5.6"
vue-server-renderer@^2.6.12:
version "2.7.10"
resolved "https://registry.yarnpkg.com/vue-server-renderer/-/vue-server-renderer-2.7.10.tgz#e73241c879fcc81de91882ceff135a40f756377c"
integrity sha512-hvlnyTZmDmnI7IpQE5YwIwexPi6yJq8eeNTUgLycPX3uhuEobygAQklHoeVREvwNKcET/MnVOtjF4c7t7mw6CQ==
version "2.7.11"
resolved "https://registry.yarnpkg.com/vue-server-renderer/-/vue-server-renderer-2.7.11.tgz#b27ff9114b3c5a73a64e379a2b195394d202d3f6"
integrity sha512-KQe79LrrgvJ2c5PqnhjKta45B64jyOWGOK34VIiujKzsYAN5ynBNYr1E0NaqcobIr/drCnA9S6LZg/rBprfm4w==
dependencies:
chalk "^4.1.2"
hash-sum "^2.0.0"
@ -17211,9 +17388,9 @@ vue-template-compiler@2.6.14:
he "^1.1.0"
vue-template-compiler@^2.6.12, vue-template-compiler@^2.6.14:
version "2.7.10"
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.7.10.tgz#9e20f35b2fdccacacf732dd7dedb49bf65f4556b"
integrity sha512-QO+8R9YRq1Gudm8ZMdo/lImZLJVUIAM8c07Vp84ojdDAf8HmPJc7XB556PcXV218k2AkKznsRz6xB5uOjAC4EQ==
version "2.7.11"
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.7.11.tgz#e18e8e0dcb2647e8d6ff2ede8a3a92e677ff8dea"
integrity sha512-17QnXkFiBLOH3gGCA3nWAWpmdlTjOWLyP/2eg5ptgY1OvDBuIDGOW9FZ7ZSKmF1UFyf56mLR3/E1SlCTml1LWQ==
dependencies:
de-indent "^1.0.2"
he "^1.2.0"
@ -17238,7 +17415,7 @@ vue@2.6.14:
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.14.tgz#e51aa5250250d569a3fbad3a8a5a687d6036e235"
integrity sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==
vue@^2.0.0, vue@^2.6.10, vue@^2.6.12:
vue@^2.0.0, vue@^2.6.10:
version "2.7.10"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.7.10.tgz#ae516cc6c88e1c424754468844218fdd5e280f40"
integrity sha512-HmFC70qarSHPXcKtW8U8fgIkF6JGvjEmDiVInTkKZP0gIlEPhlVlcJJLkdGIDiNkIeA2zJPQTWJUI4iWe+AVfg==
@ -17246,6 +17423,14 @@ vue@^2.0.0, vue@^2.6.10, vue@^2.6.12:
"@vue/compiler-sfc" "2.7.10"
csstype "^3.1.0"
vue@^2.6.12:
version "2.7.11"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.7.11.tgz#e051d54a131e7094c3ea3a86b2c6ecf25f8d0002"
integrity sha512-VPAW5QelT7Tx6UoSw/cwx/jDROOKAK1y/Q0o7HkmVJ1WAypE7w1+UoFa+KsGxy1aYdHPU1oODB3vR6XwSfVhDg==
dependencies:
"@vue/compiler-sfc" "2.7.11"
csstype "^3.1.0"
vuedraggable@2.24.3:
version "2.24.3"
resolved "https://registry.yarnpkg.com/vuedraggable/-/vuedraggable-2.24.3.tgz#43c93849b746a24ce503e123d5b259c701ba0d19"