Switch to history mode in app (#594)

* Switch to history mode in pp

* Remove broken tests

* Install asset manifest

* Only build to modern

* Remove override files in favor of settings

* Build assets json

* Use dynamic routing based on api basepath

* Remove override files

* Set webpack public path based on passed window var

* Fix tests
This commit is contained in:
Rijk van Zanten
2020-05-19 17:16:44 -04:00
committed by GitHub
parent c552364d1f
commit 0c745a6eee
15 changed files with 96 additions and 122 deletions

View File

@@ -1,4 +1,12 @@
module.exports = {
presets: [['@vue/cli-plugin-babel/preset', { jsx: false }]],
presets: [
[
'@vue/app',
{
targets: { esmodules: true },
polyfills: [],
},
],
],
plugins: ['@babel/plugin-proposal-optional-chaining'],
};

View File

@@ -6,7 +6,7 @@
"author": "Rijk van Zanten <rijk@rngr.org>",
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build --modern",
"build": "vue-cli-service build",
"test": "vue-cli-service test:unit",
"lint": "vue-cli-service lint",
"lint:styles": "stylelint \"**/*.{vue,scss}\"",
@@ -104,6 +104,7 @@
"vue-template-compiler": "^2.6.10",
"vuepress": "^1.5.0",
"webpack": "^4.43.0",
"webpack-assets-manifest": "^3.1.1",
"webpack-merge": "^4.2.2"
},
"gitHooks": {

View File

@@ -7,8 +7,6 @@
<link rel="shortcut icon" href="favicon.ico">
<link rel="manifest" href="manifest.webmanifest" />
<title>Directus</title>
<link rel="stylesheet" href="style.css" />
</head>
<body class="auto">
<noscript>
@@ -16,6 +14,5 @@
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<script src="script.js"></script>
</body>
</html>

View File

@@ -1,5 +0,0 @@
/*
* This is a custom script file that is loaded for each instance on each page.
*
* Use to it add whatever client scripts your project might need
*/

View File

@@ -1,7 +0,0 @@
/*
Place your custom style overrides here.
NOTE: The main application's styling is included through JavaScript. Therefore, it gets added to
the head _after_ this file. If you want to override the globally used CSS variables, you can either
use !important, or increase the specificity from :root
*/

View File

@@ -1,6 +1,6 @@
import Vue from 'vue';
import VueCompositionAPI from '@vue/composition-api';
import { onRequest, onResponse, onError, getRootPath, RequestError } from './api';
import { onRequest, onResponse, onError, RequestError } from './api';
import * as auth from '@/auth';
import { useRequestsStore } from '@/stores/requests';
@@ -29,18 +29,6 @@ describe('API', () => {
window = Object.create(window);
});
it('Calculates the correct API root URL based on window', () => {
Object.defineProperty(window, 'location', {
value: {
pathname: '/api/nested/admin',
},
writable: true,
});
const result = getRootPath();
expect(result).toBe('/api/nested/');
});
it('Calls startRequest on the store on any request', () => {
const store = useRequestsStore({});
const spy = jest.spyOn(store, 'startRequest');

View File

@@ -1,6 +1,7 @@
import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';
import { useRequestsStore } from '@/stores/requests';
import { LogoutReason, logout, checkAuth } from '@/auth';
import getRootPath from '@/utils/get-root-path';
const api = axios.create({
baseURL: getRootPath(),
@@ -65,12 +66,4 @@ export const onError = async (error: RequestError) => {
api.interceptors.request.use(onRequest);
api.interceptors.response.use(onResponse, onError);
export function getRootPath(): string {
const path = window.location.pathname;
const parts = path.split('/');
const adminIndex = parts.indexOf('admin');
const rootPath = parts.slice(0, adminIndex).join('/') + '/';
return rootPath;
}
export default api;

View File

@@ -1,79 +0,0 @@
import { mount, createLocalVue, Wrapper } from '@vue/test-utils';
import VueCompositionAPI from '@vue/composition-api';
import VNotice from './v-notice.vue';
import VIcon from '@/components/v-icon/';
const localVue = createLocalVue();
localVue.use(VueCompositionAPI);
localVue.component('v-icon', VIcon);
describe('Notice', () => {
let component: Wrapper<Vue>;
beforeEach(() => {
component = mount(VNotice, {
localVue,
slots: {
default: 'I like pizza',
},
});
});
it('Renders the default slot in the notice', () => {
expect(component.text()).toContain('I like pizza');
});
it('Uses the right color / icon combo for success', async () => {
component.setProps({
success: true,
});
await component.vm.$nextTick();
expect(component.classes()).toContain('success');
expect((component.vm as any).iconName).toBe('check_circle');
});
it('Uses the right color / icon combo for warning', async () => {
component.setProps({
warning: true,
});
await component.vm.$nextTick();
expect(component.classes()).toContain('warning');
expect((component.vm as any).iconName).toBe('warning');
});
it('Uses the right color / icon combo for danger', async () => {
component.setProps({
danger: true,
});
await component.vm.$nextTick();
expect(component.classes()).toContain('danger');
expect((component.vm as any).iconName).toBe('error');
});
it('Defaults to success if all props are given', async () => {
component.setProps({
success: true,
warning: true,
danger: true,
});
await component.vm.$nextTick();
expect((component.vm as any).iconName).toBe('check_circle');
expect(component.classes()).toContain('success');
});
it('Allows setting a custom icon', async () => {
component.setProps({
icon: 'person',
});
await component.vm.$nextTick();
expect((component.vm as any).iconName).toBe('person');
});
});

View File

@@ -1,3 +1,5 @@
__webpack_public_path__ = (window as any).$directusAssetBasePath || '/admin/';
import Vue from 'vue';
import { version } from '../package.json';

View File

@@ -11,6 +11,8 @@ import useAppStore from '@/stores/app';
import useUserStore from '@/stores/user';
import PrivateNotFoundRoute from '@/routes/private-not-found';
import getRootPath from '@/utils/get-root-path';
export const onBeforeEnterProjectChooser: NavigationGuard = (to, from, next) => {
const projectsStore = useProjectsStore();
projectsStore.state.currentProjectKey = null;
@@ -43,6 +45,9 @@ export const defaultRoutes: RouteConfig[] = [
name: 'login',
path: '/:project/login',
component: LoginRoute,
props: (route) => ({
ssoErrorCode: route.query.error,
}),
meta: {
public: true,
},
@@ -83,13 +88,18 @@ export const defaultRoutes: RouteConfig[] = [
];
const router = new VueRouter({
mode: 'hash',
mode: 'history',
base: getRootPath() + 'admin/',
routes: defaultRoutes,
});
export function replaceRoutes(routeFilter: (routes: RouteConfig[]) => RouteConfig[]): void {
const newRoutes = routeFilter([...defaultRoutes]);
const newRouter = new VueRouter({ routes: newRoutes });
const newRouter = new VueRouter({
mode: 'history',
base: getRootPath() + 'admin/',
routes: newRoutes,
});
// @ts-ignore - Matcher is not officially part of the public API (https://github.com/vuejs/vue-router/issues/2844#issuecomment-509529927)
router.matcher = newRouter.matcher;

View File

@@ -0,0 +1,15 @@
import getRootPath from './get-root-path';
describe('Utils / get root path', () => {
it('Calculates the correct API root URL based on window', () => {
Object.defineProperty(window, 'location', {
value: {
pathname: '/api/nested/admin',
},
writable: true,
});
const result = getRootPath();
expect(result).toBe('/api/nested/');
});
});

View File

@@ -0,0 +1,7 @@
export default function getRootPath(): string {
const path = window.location.pathname;
const parts = path.split('/');
const adminIndex = parts.indexOf('admin');
const rootPath = parts.slice(0, adminIndex).join('/') + '/';
return rootPath;
}

View File

@@ -0,0 +1,4 @@
import getRootPath from './get-root-path';
export default getRootPath;
export { getRootPath };

View File

@@ -1,3 +1,8 @@
/* @tslint:disable */
/* eslint-disable */
const WebpackAssetsManifest = require('webpack-assets-manifest');
if (!process.env.API_URL && process.env.NODE_ENV === 'development') {
console.log(`
⚠️ No API URL passed. Using the demo API as a fallback.
@@ -6,7 +11,7 @@ if (!process.env.API_URL && process.env.NODE_ENV === 'development') {
module.exports = {
lintOnSave: false,
publicPath: process.env.NODE_ENV === 'production' ? '' : '/admin/',
publicPath: '/admin/',
devServer: {
allowedHosts: ['localhost', '.gitpod.io'],
@@ -15,10 +20,15 @@ module.exports = {
'/': {
target: process.env.API_URL ? process.env.API_URL : 'https://demo.directus.io/',
changeOrigin: true,
bypass: (req) => (req.url.startsWith('/admin') ? req.url : null),
},
},
},
configureWebpack: {
plugins: [new WebpackAssetsManifest({ output: 'assets.json' })],
},
// There are so many chunks (from all the interfaces / layouts) that we need to make sure to not
// prefetch them all. Prefetching them all will cause the server to apply rate limits in most cases
chainWebpack: (config) => {

View File

@@ -4240,7 +4240,7 @@ ccount@^1.0.0:
resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.5.tgz#ac82a944905a65ce204eb03023157edf29425c17"
integrity sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw==
chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2:
chalk@2.4.2, chalk@^2.0, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -9369,6 +9369,16 @@ lodash.defaultsdeep@^4.6.1:
resolved "https://registry.yarnpkg.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz#512e9bd721d272d94e3d3a63653fa17516741ca6"
integrity sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==
lodash.get@^4.0:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
lodash.has@^4.0:
version "4.5.2"
resolved "https://registry.yarnpkg.com/lodash.has/-/lodash.has-4.5.2.tgz#d19f4dc1095058cccbe2b0cdf4ee0fe4aa37c862"
integrity sha1-0Z9NwQlQWMzL4rDN9O4P5Ko3yGI=
lodash.kebabcase@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36"
@@ -10044,6 +10054,13 @@ mkdirp@0.x, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1, mkdirp@~0.5.x:
dependencies:
minimist "^1.2.5"
mkdirp@^0.5:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
dependencies:
minimist "^1.2.5"
mockdate@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/mockdate/-/mockdate-2.0.5.tgz#70c6abf9ed4b2dae65c81dfc170dd1a5cec53620"
@@ -15309,6 +15326,19 @@ webidl-conversions@^4.0.2:
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
webpack-assets-manifest@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/webpack-assets-manifest/-/webpack-assets-manifest-3.1.1.tgz#39bbc3bf2ee57fcd8ba07cda51c9ba4a3c6ae1de"
integrity sha512-JV9V2QKc5wEWQptdIjvXDUL1ucbPLH2f27toAY3SNdGZp+xSaStAgpoMcvMZmqtFrBc9a5pTS1058vxyMPOzRQ==
dependencies:
chalk "^2.0"
lodash.get "^4.0"
lodash.has "^4.0"
mkdirp "^0.5"
schema-utils "^1.0.0"
tapable "^1.0.0"
webpack-sources "^1.0.0"
webpack-bundle-analyzer@^3.6.1:
version "3.6.1"
resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.6.1.tgz#bdb637c2304424f2fbff9a950c7be42a839ae73b"
@@ -15419,7 +15449,7 @@ webpack-merge@^4.1.2, webpack-merge@^4.2.2:
dependencies:
lodash "^4.17.15"
webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3:
webpack-sources@^1.0.0, webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3:
version "1.4.3"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"
integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==