]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
refactor: move playground
authorEduardo San Martin Morote <posva13@gmail.com>
Fri, 10 Jun 2022 16:49:04 +0000 (18:49 +0200)
committerEduardo San Martin Morote <posva@users.noreply.github.com>
Thu, 30 Jun 2022 07:59:00 +0000 (09:59 +0200)
36 files changed:
.prettierignore
package.json
packages/playground/App.vue [moved from playground/App.vue with 99% similarity]
packages/playground/env.d.ts [moved from playground/shim.d.ts with 57% similarity]
packages/playground/index.html [moved from playground/index.html with 96% similarity]
packages/playground/package.json [new file with mode: 0644]
packages/playground/src/App.vue [new file with mode: 0644]
packages/playground/src/AppLink.vue [moved from playground/AppLink.vue with 91% similarity]
packages/playground/src/api/index.js [new file with mode: 0644]
packages/playground/src/api/index.ts [moved from playground/api/index.ts with 100% similarity]
packages/playground/src/main.js [new file with mode: 0644]
packages/playground/src/main.ts [moved from playground/main.ts with 84% similarity]
packages/playground/src/router.js [new file with mode: 0644]
packages/playground/src/router.ts [moved from playground/router.ts with 91% similarity]
packages/playground/src/scrollWaiter.js [new file with mode: 0644]
packages/playground/src/scrollWaiter.ts [moved from playground/scrollWaiter.ts with 100% similarity]
packages/playground/src/store.js [new file with mode: 0644]
packages/playground/src/store.ts [moved from playground/store.ts with 100% similarity]
packages/playground/src/views/ComponentWithData.vue [moved from playground/views/ComponentWithData.vue with 93% similarity]
packages/playground/src/views/Dynamic.vue [moved from playground/views/Dynamic.vue with 100% similarity]
packages/playground/src/views/Generic.vue [moved from playground/views/Generic.vue with 100% similarity]
packages/playground/src/views/GuardedWithLeave.vue [moved from playground/views/GuardedWithLeave.vue with 91% similarity]
packages/playground/src/views/Home.vue [moved from playground/views/Home.vue with 100% similarity]
packages/playground/src/views/LongView.vue [moved from playground/views/LongView.vue with 93% similarity]
packages/playground/src/views/Nested.vue [moved from playground/views/Nested.vue with 100% similarity]
packages/playground/src/views/NestedWithId.vue [moved from playground/views/NestedWithId.vue with 100% similarity]
packages/playground/src/views/NotFound.vue [moved from playground/views/NotFound.vue with 86% similarity]
packages/playground/src/views/RepeatedParams.vue [moved from playground/views/RepeatedParams.vue with 95% similarity]
packages/playground/src/views/User.vue [moved from playground/views/User.vue with 100% similarity]
packages/playground/tsconfig.config.json [new file with mode: 0644]
packages/playground/tsconfig.json [new file with mode: 0644]
packages/playground/vite.config.ts [moved from playground/vite.config.js with 50% similarity]
packages/router/package.json
packages/router/src/RouterLink.ts
playground/tsconfig.json [deleted file]
pnpm-lock.yaml

index 197fddc69e0e60aa5e151948009fedb70ea32135..0442789cf772db25c68ff89cfa8263c5dc357ce7 100644 (file)
@@ -1,2 +1,3 @@
-e2e/__build__
+__build__
 dist
+coverage
index 8b87ead2c50d921c915f765e415e523bb84aa447..fd18d089c3868bfdb11c88c46048d22a3ad37db8 100644 (file)
     "docs:build": "pnpm run -r docs:build --filter ./packages/docs",
     "play": "pnpm run -r play",
     "build:size": "pnpm run -r build:size",
-    "lint": "prettier -c --parser typescript \"packages/*/{src,__tests__,e2e}/**/*.[jt]s?(x)\"",
-    "lint:fix": "pnpm run lint --write",
+    "lint": "pnpm run lint:script && pnpm run lint:html",
+    "lint:script": "prettier -c --parser typescript \"packages/*/{src,__tests__,e2e}/**/*.[jt]s?(x)\"",
+    "lint:html": "prettier -c --parser html \"packages/**/*.html\"",
+    "lint:fix": "pnpm run lint:script --write && pnpm run lint:html --write",
     "test": "pnpm run -r test"
   },
   "devDependencies": {
+    "@vue/compiler-sfc": "^3.2.31",
+    "@vue/server-renderer": "^3.2.37",
+    "@vue/test-utils": "^2.0.0-rc.3",
     "brotli": "^1.3.3",
     "chalk": "^4.1.2",
     "enquirer": "^2.3.6",
     "lint-staged": "^13.0.0",
     "minimist": "^1.2.6",
     "p-series": "^3.0.0",
+    "prettier": "^2.4.1",
     "semver": "^7.3.7",
+    "typescript": "~4.7.2",
+    "vue": "^3.2.37",
+    "vue-tsc": "^0.37.2",
     "yorkie": "^2.0.0"
   },
   "gitHooks": {
similarity index 99%
rename from playground/App.vue
rename to packages/playground/App.vue
index 1e25a4d864a3442c22f3144c480adbcbf036330f..050146168f726975860e9fe4d33a099ffff9e81e 100644 (file)
 <script>
 import { defineComponent, inject, computed, ref } from 'vue'
 import { scrollWaiter } from './scrollWaiter'
-import { useLink, useRoute } from '../src'
+import { useLink, useRoute } from 'vue-router'
 import AppLink from './AppLink.vue'
 
 export default defineComponent({
similarity index 57%
rename from playground/shim.d.ts
rename to packages/playground/env.d.ts
index 996ea4df28c1951b2f9e74fadd1f0ba9adc1d2e9..befb0fbf18cbdf7dd3ad73e0fd949e151da62a81 100644 (file)
@@ -1,3 +1,6 @@
+/// <reference types="vite/client" />
+/// <reference path="vue-router/global.d.ts"/>
+
 declare module '*.vue' {
   import { Component } from 'vue'
   var component: Component
similarity index 96%
rename from playground/index.html
rename to packages/playground/index.html
index e603ecca3153435c09325cf65a3c7da0adcd2386..665a0ec9eb40ff96485ff2efec6c4f1eef0d5cb2 100644 (file)
@@ -63,9 +63,8 @@
       }
     </style>
   </head>
-
   <body>
     <div id="app"></div>
-    <script type="module" src="/main.ts"></script>
+    <script type="module" src="/src/main.ts"></script>
   </body>
 </html>
diff --git a/packages/playground/package.json b/packages/playground/package.json
new file mode 100644 (file)
index 0000000..48a2d28
--- /dev/null
@@ -0,0 +1,22 @@
+{
+  "name": "@vue/router-playground",
+  "private": true,
+  "version": "0.0.0",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview --port 4173"
+  },
+  "dependencies": {
+    "vue": "^3.2.36"
+  },
+  "devDependencies": {
+    "@types/node": "^16.11.36",
+    "@vitejs/plugin-vue": "^2.3.3",
+    "@vue/tsconfig": "^0.1.3",
+    "typescript": "~4.7.2",
+    "vite": "^2.9.9",
+    "vue-router": "workspace:*",
+    "vue-tsc": "^0.37.2"
+  }
+}
diff --git a/packages/playground/src/App.vue b/packages/playground/src/App.vue
new file mode 100644 (file)
index 0000000..0501461
--- /dev/null
@@ -0,0 +1,233 @@
+<template>
+  <div>
+    <pre>{{ currentLocation }}</pre>
+    <section class="info">
+      Name:
+      <pre id="name">{{ currentLocation.name }}</pre>
+    </section>
+
+    <section class="info">
+      Params:
+      <pre id="params">{{ currentLocation.params }}</pre>
+    </section>
+
+    <section class="info">
+      Query:
+      <pre id="query">{{ currentLocation.query }}</pre>
+    </section>
+
+    <section class="info">
+      Hash:
+      <pre id="hash">{{ currentLocation.hash }}</pre>
+    </section>
+
+    <section class="info">
+      FullPath:
+      <pre id="fullPath">{{ currentLocation.fullPath }}</pre>
+    </section>
+
+    <section class="info">
+      path:
+      <pre id="path">{{ currentLocation.path }}</pre>
+    </section>
+
+    <hr />
+
+    <label>
+      <input type="checkbox" v-model="state.cancelNextNavigation" /> Cancel Next
+      Navigation
+    </label>
+    <ul>
+      <li>
+        <router-link to="/n/%E2%82%AC">/n/%E2%82%AC</router-link>
+      </li>
+      <li>
+        <router-link :to="{ name: 'docs', params: { id: '€uro' } }"
+          >/docs/€uro (object)</router-link
+        >
+      </li>
+      <li>
+        <router-link :to="{ path: '/', query: { currency: '€uro', é: 'e' } }"
+          >/currency=€uro&é=e (object)</router-link
+        >
+      </li>
+      <li>
+        <router-link to="/documents/€">/n/€</router-link>
+      </li>
+      <li>
+        <a href="/documents/%E2%82%AC">/documents/%E2%82%AC (force reload)</a>
+      </li>
+      <li>
+        <a href="/documents/€">/documents/€ (force reload): not valid tho</a>
+      </li>
+      <li>
+        <router-link to="/home">Home (redirects)</router-link>
+      </li>
+      <li>
+        <router-link to="/">Home</router-link>
+      </li>
+      <li>
+        <AppLink to="/">AppLink Home</AppLink>
+      </li>
+      <li>
+        <router-link to="/always-redirect">/always-redirect</router-link>
+      </li>
+      <li>
+        <router-link to="/children">/children</router-link>
+      </li>
+      <li>
+        <router-link to="/children/alias">/children/alias</router-link>
+      </li>
+      <li>
+        <router-link :to="{ name: 'default-child' }"
+          >/children (child named)</router-link
+        >
+      </li>
+      <li>
+        <router-link :to="{ name: 'WithChildren' }"
+          >/children (parent named)</router-link
+        >
+      </li>
+      <li>
+        <router-link to="/children/a">/children/a</router-link>
+      </li>
+      <li>
+        <router-link to="/children/b">/children/b</router-link>
+      </li>
+      <li>
+        <router-link to="/children/b/a2">/children/b/a2</router-link>
+      </li>
+      <li>
+        <router-link to="/children/b/b2">/children/b/b2</router-link>
+      </li>
+      <li>
+        <router-link to="/nested">/nested</router-link>
+      </li>
+      <li>
+        <router-link to="/anidado">/anidado</router-link>
+      </li>
+      <li>
+        <router-link to="/long-0">/long-0</router-link>
+      </li>
+      <li>
+        <router-link to="/users/5">/users/5</router-link>
+      </li>
+      <li>
+        <router-link
+          :to="{
+            name: 'user',
+            params: { id: '' + (Number(currentLocation.params.id || 0) + 1) },
+          }"
+          >/users/{{ Number(currentLocation.params.id || 0) + 1 }}</router-link
+        >
+      </li>
+      <li>
+        <router-link to="/with-data">/with-data</router-link>
+      </li>
+      <li>
+        <router-link to="/cant-leave">/cant-leave</router-link>
+      </li>
+      <li>
+        <router-link :to="{ name: 'docs', params: { id: 'é' } }"
+          >/docs/é</router-link
+        >
+      </li>
+      <li>
+        <router-link to="/rep">/rep</router-link>
+      </li>
+      <li>
+        <router-link to="/rep/a">/rep/a</router-link>
+      </li>
+      <li>
+        <router-link to="/rep/a/b">/rep/a/b</router-link>
+      </li>
+      <li>
+        <router-link to="/parent/1">/parent/1</router-link>
+      </li>
+      <li>
+        <router-link to="/p/1">/p/1</router-link>
+      </li>
+      <li>
+        <router-link to="/parent/1/as-absolute-a"
+          >/parent/1/as-absolute-a</router-link
+        >
+      </li>
+      <li>
+        <router-link to="/p/1/as-absolute-a">/p/1/as-absolute-a</router-link>
+      </li>
+      <li>
+        <router-link to="/p_1/absolute-a">/p_1/absolute-a</router-link>
+      </li>
+    </ul>
+    <button @click="toggleViewName">Toggle view</button>
+    <Suspense>
+      <template #default>
+        <router-view :name="viewName" v-slot="{ Component, route }">
+          <transition
+            :name="route.meta.transition || 'fade'"
+            mode="out-in"
+            @before-enter="flushWaiter"
+            @before-leave="setupWaiter"
+          >
+            <keep-alive>
+              <component
+                :is="Component"
+                :key="route.name === 'repeat' ? route.path : undefined"
+              />
+            </keep-alive>
+          </transition>
+        </router-view>
+      </template>
+      <template #fallback> Loading... </template>
+    </Suspense>
+  </div>
+</template>
+
+<script>
+import { defineComponent, inject, computed, ref } from 'vue'
+import { scrollWaiter } from './scrollWaiter'
+import { useLink, useRoute } from 'vue-router'
+import AppLink from './AppLink.vue'
+
+export default defineComponent({
+  name: 'App',
+  components: { AppLink },
+  setup() {
+    const route = useRoute()
+    const state = inject('state')
+    const viewName = ref('default')
+
+    useLink({ to: '/' })
+    useLink({ to: '/documents/hello' })
+    useLink({ to: '/children' })
+
+    const currentLocation = computed(() => {
+      const { matched, ...rest } = route
+      return rest
+    })
+
+    function flushWaiter() {
+      scrollWaiter.flush()
+    }
+    function setupWaiter() {
+      scrollWaiter.add()
+    }
+
+    const nextUserLink = computed(
+      () => '/users/' + String((Number(route.value.params.id) || 0) + 1)
+    )
+
+    return {
+      currentLocation,
+      nextUserLink,
+      state,
+      flushWaiter,
+      setupWaiter,
+      viewName,
+      toggleViewName() {
+        viewName.value = viewName.value === 'default' ? 'other' : 'default'
+      },
+    }
+  },
+})
+</script>
similarity index 91%
rename from playground/AppLink.vue
rename to packages/playground/src/AppLink.vue
index b28476a09cb688bbabc397379a53e03aa5cc4b1d..8132ff20a51c96a6e2eab7494409a868dcef4c47 100644 (file)
 </template>
 
 <script>
-import { RouterLinkImpl } from '../src/RouterLink'
+import { RouterLink, START_LOCATION, useLink, useRoute } from 'vue-router'
 import { computed, defineComponent, toRefs } from 'vue'
-import { START_LOCATION, useLink, useRoute } from '../src'
 
 export default defineComponent({
   props: {
-    ...RouterLinkImpl.props,
+    ...RouterLink.props,
     disabled: Boolean,
   },
 
diff --git a/packages/playground/src/api/index.js b/packages/playground/src/api/index.js
new file mode 100644 (file)
index 0000000..695b73a
--- /dev/null
@@ -0,0 +1,8 @@
+export let delay = (t = 100) => new Promise(resolve => setTimeout(resolve, t))
+export async function getData() {
+  await delay(500)
+  return {
+    message: 'Hello',
+    time: Date.now(),
+  }
+}
diff --git a/packages/playground/src/main.js b/packages/playground/src/main.js
new file mode 100644 (file)
index 0000000..265fd68
--- /dev/null
@@ -0,0 +1,17 @@
+// necessary for webpack
+import { createApp } from 'vue'
+import { router, routerHistory } from './router'
+import { globalState } from './store'
+import App from './App.vue'
+// for testing purposes
+window.h = routerHistory
+window.r = router
+const app = createApp(App)
+app.mixin({
+  beforeRouteEnter() {
+    console.log('mixin enter')
+  },
+})
+app.provide('state', globalState)
+app.use(router)
+window.vm = app.mount('#app')
similarity index 84%
rename from playground/main.ts
rename to packages/playground/src/main.ts
index 92934c8bd717b616568f090a388fea7ac64a6414..ce131cb69ac4fb4a75484583d3d1859dd28139e0 100644 (file)
@@ -1,6 +1,6 @@
 // necessary for webpack
-///<reference path="../src/global.d.ts"/>
-import { createApp, ComponentPublicInstance } from 'vue'
+import { createApp } from 'vue'
+import type { ComponentPublicInstance } from 'vue'
 import { router, routerHistory } from './router'
 import { globalState } from './store'
 import App from './App.vue'
diff --git a/packages/playground/src/router.js b/packages/playground/src/router.js
new file mode 100644 (file)
index 0000000..0134abf
--- /dev/null
@@ -0,0 +1,401 @@
+import { createRouter, createWebHistory, RouterView } from 'vue-router'
+import Home from './views/Home.vue'
+import Nested from './views/Nested.vue'
+import NestedWithId from './views/NestedWithId.vue'
+import Dynamic from './views/Dynamic.vue'
+import User from './views/User.vue'
+import NotFound from './views/NotFound.vue'
+const component = () => {
+  console.log('fetching component')
+  return import('./views/Generic.vue')
+}
+import LongView from './views/LongView.vue'
+import GuardedWithLeave from './views/GuardedWithLeave.vue'
+import ComponentWithData from './views/ComponentWithData.vue'
+import { globalState } from './store'
+import { scrollWaiter } from './scrollWaiter'
+import RepeatedParams from './views/RepeatedParams.vue'
+import { h } from 'vue'
+let removeRoute
+const TransparentWrapper = () => h(RouterView)
+TransparentWrapper.displayName = 'NestedView'
+export const routerHistory = createWebHistory()
+export const router = createRouter({
+  history: routerHistory,
+  strict: true,
+  routes: [
+    { path: '/home', redirect: '/' },
+    {
+      path: '/',
+      components: { default: Home, other: component },
+      props: { default: to => ({ waited: to.meta.waitedFor }) },
+    },
+    {
+      path: '/always-redirect',
+      redirect: () => ({
+        name: 'user',
+        params: { id: String(Math.round(Math.random() * 100)) },
+      }),
+    },
+    { path: '/users/:id', name: 'user', component: User, props: true },
+    { path: '/documents/:id', name: 'docs', component: User, props: true },
+    { path: '/optional/:id?', name: 'optional', component: User, props: true },
+    { path: encodeURI('/n/€'), name: 'euro', component },
+    { path: '/n/:n', name: 'increment', component },
+    { path: '/multiple/:a/:b', name: 'multiple', component },
+    { path: '/long-:n', name: 'long', component: LongView },
+    {
+      path: '/lazy',
+      meta: { transition: 'slide-left' },
+      component: async () => {
+        await delay(500)
+        return component()
+      },
+    },
+    {
+      path: '/with-guard/:n',
+      name: 'guarded',
+      component,
+      beforeEnter(to, from, next) {
+        if (to.params.n !== 'valid') next(false)
+        next()
+      },
+    },
+    { path: '/cant-leave', component: GuardedWithLeave },
+    {
+      path: '/children',
+      name: 'WithChildren',
+      component: Nested,
+      children: [
+        { path: '', alias: 'alias', name: 'default-child', component: Nested },
+        { path: 'a', name: 'a-child', component: Nested },
+        {
+          path: 'b',
+          name: 'b-child',
+          component: Nested,
+          children: [
+            { path: '', component: Nested },
+            { path: 'a2', component: Nested },
+            { path: 'b2', component: Nested },
+          ],
+        },
+      ],
+    },
+    { path: '/with-data', component: ComponentWithData, name: 'WithData' },
+    { path: '/rep/:a*', component: RepeatedParams, name: 'repeat' },
+    { path: '/:data(.*)', component: NotFound, name: 'NotFound' },
+    {
+      path: '/nested',
+      alias: '/anidado',
+      component: Nested,
+      name: 'Nested',
+      children: [
+        {
+          path: 'nested',
+          alias: 'a',
+          name: 'NestedNested',
+          component: Nested,
+          children: [
+            {
+              name: 'NestedNestedNested',
+              path: 'nested',
+              component: Nested,
+            },
+          ],
+        },
+        {
+          path: 'other',
+          alias: 'otherAlias',
+          component: Nested,
+          name: 'NestedOther',
+        },
+        {
+          path: 'also-as-absolute',
+          alias: '/absolute',
+          name: 'absolute-child',
+          component: Nested,
+        },
+      ],
+    },
+    {
+      path: '/parent/:id',
+      name: 'parent',
+      component: NestedWithId,
+      props: true,
+      alias: '/p/:id',
+      children: [
+        // empty child
+        { path: '', component },
+        // child with absolute path. we need to add an `id` because the parent needs it
+        { path: '/p_:id/absolute-a', alias: 'as-absolute-a', component },
+        // same as above but the alias is absolute
+        { path: 'as-absolute-b', alias: '/p_:id/absolute-b', component },
+      ],
+    },
+    {
+      path: '/dynamic',
+      name: 'dynamic',
+      component: Nested,
+      end: false,
+      strict: true,
+      beforeEnter(to, from, next) {
+        if (!removeRoute) {
+          removeRoute = router.addRoute('dynamic', {
+            path: 'child',
+            component: Dynamic,
+          })
+          next(to.fullPath)
+        } else next()
+      },
+    },
+    {
+      path: '/admin',
+      component: TransparentWrapper,
+      children: [
+        { path: '', component },
+        { path: 'dashboard', component },
+        { path: 'settings', component },
+      ],
+    },
+  ],
+  async scrollBehavior(to, from, savedPosition) {
+    await scrollWaiter.wait()
+    if (savedPosition) {
+      return savedPosition
+    } else {
+      if (to.matched.every((record, i) => from.matched[i] !== record))
+        return { left: 0, top: 0 }
+    }
+    // leave scroll as it is by not returning anything
+    // https://github.com/Microsoft/TypeScript/issues/18319
+    return false
+  },
+})
+const myRouter = createRouter({
+  history: routerHistory,
+  strict: true,
+  routes: [
+    { path: '/home', redirect: '/' },
+    {
+      path: '/',
+      components: { default: Home, other: component },
+      props: { default: to => ({ waited: to.meta.waitedFor }) },
+    },
+    {
+      path: '/always-redirect',
+      redirect: () => ({
+        name: 'user',
+        params: { id: String(Math.round(Math.random() * 100)) },
+      }),
+    },
+    { path: '/users/:id', name: 'user', component: User, props: true },
+    { path: '/documents/:id', name: 'docs', component: User, props: true },
+    { path: '/optional/:id?', name: 'optional', component: User, props: true },
+    { path: encodeURI('/n/€'), name: 'euro', component },
+    { path: '/n/:n', name: 'increment', component },
+    { path: '/multiple/:a/:b', name: 'multiple', component },
+    { path: '/long-:n', name: 'long', component: LongView },
+    {
+      path: '/lazy',
+      meta: { transition: 'slide-left' },
+      component: async () => {
+        await delay(500)
+        return component()
+      },
+    },
+    {
+      path: '/with-guard/:n',
+      name: 'guarded',
+      component,
+      beforeEnter(to, from, next) {
+        if (to.params.n !== 'valid') next(false)
+        next()
+      },
+    },
+    { path: '/cant-leave', component: GuardedWithLeave },
+    {
+      path: '/children',
+      name: 'WithChildren',
+      component: Nested,
+      children: [
+        { path: '', alias: 'alias', name: 'default-child', component: Nested },
+        { path: 'a', name: 'a-child', component: Nested },
+        {
+          path: 'b',
+          name: 'b-child',
+          component: Nested,
+          children: [
+            { path: '', component: Nested },
+            { path: 'a2', component: Nested },
+            { path: 'b2', component: Nested },
+          ],
+        },
+      ],
+    },
+    { path: '/with-data', component: ComponentWithData, name: 'WithData' },
+    { path: '/rep/:a*', component: RepeatedParams, name: 'repeat' },
+    // { path: '/:data(.*)', component: NotFound, name: 'NotFound' },
+    {
+      path: '/nested',
+      alias: '/anidado',
+      component: Nested,
+      name: 'Nested',
+      children: [
+        {
+          path: 'nested',
+          alias: 'a',
+          name: 'NestedNested',
+          component: Nested,
+          children: [
+            {
+              name: 'NestedNestedNested',
+              path: 'nested',
+              component: Nested,
+            },
+          ],
+        },
+        {
+          path: 'other',
+          alias: 'otherAlias',
+          component: Nested,
+          name: 'NestedOther',
+        },
+        {
+          path: 'also-as-absolute',
+          alias: '/absolute',
+          name: 'absolute-child',
+          component: Nested,
+        },
+      ],
+    },
+    {
+      path: '/parent/:id',
+      name: 'parent',
+      component: NestedWithId,
+      props: true,
+      alias: '/p/:id',
+      children: [
+        // empty child
+        { path: '', component },
+        // child with absolute path. we need to add an `id` because the parent needs it
+        { path: '/p_:id/absolute-a', alias: 'as-absolute-a', component },
+        // same as above but the alias is absolute
+        { path: 'as-absolute-b', alias: '/p_:id/absolute-b', component },
+      ],
+    },
+    {
+      path: '/dynamic',
+      name: 'dynamic',
+      component: Nested,
+      end: false,
+      strict: true,
+      beforeEnter(to, from, next) {
+        if (!removeRoute) {
+          removeRoute = router.addRoute('dynamic', {
+            path: 'child',
+            component: Dynamic,
+          })
+          next(to.fullPath)
+        } else next()
+      },
+    },
+    {
+      path: '/admin',
+      component: TransparentWrapper,
+      children: [
+        { path: '', component },
+        { path: 'dashboard', component },
+        { path: 'settings', component },
+      ],
+    },
+  ],
+  async scrollBehavior(to, from, savedPosition) {
+    await scrollWaiter.wait()
+    if (savedPosition) {
+      return savedPosition
+    } else {
+      if (to.matched.every((record, i) => from.matched[i] !== record))
+        return { left: 0, top: 0 }
+    }
+    // leave scroll as it is by not returning anything
+    // https://github.com/Microsoft/TypeScript/issues/18319
+    return false
+  },
+})
+const delay = t => new Promise(resolve => setTimeout(resolve, t))
+// remove trailing slashes
+router.beforeEach(to => {
+  if (/.\/$/.test(to.path)) {
+    to.meta.redirectCode = 301
+    return to.path.replace(/\/$/, '')
+  }
+})
+router.beforeEach(async to => {
+  // console.log(`Guard from ${from.fullPath} to ${to.fullPath}`)
+  if (to.params.id === 'no-name') return false
+  const time = Number(to.query.delay)
+  if (time > 0) {
+    console.log('⏳ waiting ' + time + 'ms')
+    to.meta.waitedFor = time
+    await delay(time)
+  }
+})
+router.beforeEach(() => {
+  if (globalState.cancelNextNavigation) return false
+})
+router.afterEach((to, from) => {
+  if (to.name === from.name && to.name === 'repeat') {
+    const toDepth = to.path.split('/').length
+    const fromDepth = from.path.split('/').length
+    to.meta.transition = toDepth < fromDepth ? 'slide-right' : 'slide-left'
+  }
+})
+router.afterEach((to, from) => {
+  // console.log(
+  //   `After guard: from ${from.fullPath} to ${
+  //     to.fullPath
+  //   } | location = ${location.href.replace(location.origin, '')}`
+  // )
+})
+export function go(delta) {
+  return new Promise((resolve, reject) => {
+    function popStateListener() {
+      clearTimeout(timeout)
+    }
+    window.addEventListener('popstate', popStateListener)
+    function clearHooks() {
+      removeAfterEach()
+      removeOnError()
+      window.removeEventListener('popstate', popStateListener)
+    }
+    // if the popstate event is not called, consider this a failure
+    const timeout = setTimeout(() => {
+      clearHooks()
+      reject(new Error('Failed to use router.go()'))
+      // using 0 leads to false positives
+    }, 1)
+    const removeAfterEach = router.afterEach((_to, _from, failure) => {
+      clearHooks()
+      resolve(failure)
+    })
+    const removeOnError = router.onError(err => {
+      clearHooks()
+      reject(err)
+    })
+    router.go(delta)
+  })
+}
+// @ts-expect-error
+window._go = go
+router.beforeEach(to => {
+  // console.log('second guard')
+  if (typeof to.query.to === 'string' && to.query.to) return to.query.to
+})
+const dirLog = {
+  '': '?',
+  back: '⏪',
+  forward: '⏩',
+}
+routerHistory.listen((to, from, info) => {
+  console.log(`${dirLog[info.direction]} as a ${info.type}`)
+})
similarity index 91%
rename from playground/router.ts
rename to packages/playground/src/router.ts
index fa13a1b0bc6415e019d3333651ab1203e37edbbb..7dd6701263d2d1c5e0fb01f209ff9713af2c74f9 100644 (file)
@@ -1,4 +1,4 @@
-import { createRouter, createWebHistory, RouterView } from '../src'
+import { createRouter, createWebHistory, RouterView } from 'vue-router'
 import Home from './views/Home.vue'
 import Nested from './views/Nested.vue'
 import NestedWithId from './views/NestedWithId.vue'
@@ -15,7 +15,8 @@ import ComponentWithData from './views/ComponentWithData.vue'
 import { globalState } from './store'
 import { scrollWaiter } from './scrollWaiter'
 import RepeatedParams from './views/RepeatedParams.vue'
-import { FunctionalComponent, h } from 'vue'
+import { h } from 'vue'
+import type { FunctionalComponent } from 'vue'
 let removeRoute: (() => void) | undefined
 
 const TransparentWrapper: FunctionalComponent = () => h(RouterView)
@@ -176,9 +177,9 @@ export const router = createRouter({
   },
 })
 
-// TODO: move to pnpm, workspaces, and use an alias 'vue-router' to be closer to a real project
+// router.push('/admin/dashboard')
 
-declare module '../src' {
+declare module 'vue-router' {
   export interface Config {
     Router: typeof router
   }
@@ -187,17 +188,16 @@ declare module '../src' {
 const delay = (t: number) => new Promise(resolve => setTimeout(resolve, t))
 
 // remove trailing slashes
-router.beforeEach((to, from, next) => {
+router.beforeEach(to => {
   if (/.\/$/.test(to.path)) {
     to.meta.redirectCode = 301
-    next(to.path.replace(/\/$/, ''))
-  } else next()
-  // next()
+    return to.path.replace(/\/$/, '')
+  }
 })
 
-router.beforeEach(async (to, from, next) => {
+router.beforeEach(async to => {
   // console.log(`Guard from ${from.fullPath} to ${to.fullPath}`)
-  if (to.params.id === 'no-name') return next(false)
+  if (to.params.id === 'no-name') return false
 
   const time = Number(to.query.delay)
   if (time > 0) {
@@ -205,12 +205,10 @@ router.beforeEach(async (to, from, next) => {
     to.meta.waitedFor = time
     await delay(time)
   }
-  next()
 })
 
-router.beforeEach((to, from, next) => {
-  if (globalState.cancelNextNavigation) return next(false)
-  next()
+router.beforeEach(() => {
+  if (globalState.cancelNextNavigation) return false
 })
 
 router.afterEach((to, from) => {
@@ -249,8 +247,6 @@ export function go(delta: number) {
       // using 0 leads to false positives
     }, 1)
 
-    setImmediate
-
     const removeAfterEach = router.afterEach((_to, _from, failure) => {
       clearHooks()
       resolve(failure)
@@ -267,10 +263,9 @@ export function go(delta: number) {
 // @ts-expect-error
 window._go = go
 
-router.beforeEach((to, from, next) => {
+router.beforeEach(to => {
   // console.log('second guard')
-  if (to.query.to) next(to.query.to as string)
-  else next()
+  if (typeof to.query.to === 'string' && to.query.to) return to.query.to
 })
 
 const dirLog = {
diff --git a/packages/playground/src/scrollWaiter.js b/packages/playground/src/scrollWaiter.js
new file mode 100644 (file)
index 0000000..a701c40
--- /dev/null
@@ -0,0 +1,18 @@
+class ScrollQueue {
+  resolve = null
+  promise = null
+  add() {
+    this.promise = new Promise(resolve => {
+      this.resolve = resolve
+    })
+  }
+  flush() {
+    this.resolve && this.resolve()
+    this.resolve = null
+    this.promise = null
+  }
+  async wait() {
+    await this.promise
+  }
+}
+export const scrollWaiter = new ScrollQueue()
diff --git a/packages/playground/src/store.js b/packages/playground/src/store.js
new file mode 100644 (file)
index 0000000..879edb2
--- /dev/null
@@ -0,0 +1,4 @@
+import { reactive } from 'vue'
+export const globalState = reactive({
+  cancelNextNavigation: false,
+})
similarity index 93%
rename from playground/views/ComponentWithData.vue
rename to packages/playground/src/views/ComponentWithData.vue
index 5c20790859ae148a6b89202692064e87ec19f166..e186e69374011815910d11809f7db49c9eccb923 100644 (file)
@@ -8,7 +8,7 @@
 <script>
 import { defineComponent, toRefs, reactive } from 'vue'
 import { getData, delay } from '../api'
-import { onBeforeRouteUpdate } from '../../src'
+import { onBeforeRouteUpdate } from 'vue-router'
 
 export default defineComponent({
   name: 'ComponentWithData',
similarity index 91%
rename from playground/views/GuardedWithLeave.vue
rename to packages/playground/src/views/GuardedWithLeave.vue
index 02d066270b4da25bf978cbbcfb572aefda1fe160..21469475b349cb8843f6abd1f50cb94bea3e2568 100644 (file)
@@ -8,7 +8,7 @@
 <script>
 // @ts-check
 import { defineComponent } from 'vue'
-import { onBeforeRouteLeave } from '../../src'
+import { onBeforeRouteLeave } from 'vue-router'
 
 export default defineComponent({
   name: 'GuardedWithLeave',
similarity index 93%
rename from playground/views/LongView.vue
rename to packages/playground/src/views/LongView.vue
index 8abe02e9adde3f4889a36d721e6c72c814720498..61296b01dbb1a87cd319675282c96186bc869b12 100644 (file)
@@ -15,7 +15,7 @@
 
 <script>
 import { defineComponent } from 'vue'
-import { useRoute } from '../../src'
+import { useRoute } from 'vue-router'
 
 export default defineComponent({
   name: 'LongView',
similarity index 86%
rename from playground/views/NotFound.vue
rename to packages/playground/src/views/NotFound.vue
index a902a7f8e526285d88abca27b1da1c3e3a18b196..2e2af5725a59e6b3be7ba84db734cc11f1c68194 100644 (file)
@@ -4,7 +4,7 @@
 
 <script>
 import { defineComponent } from 'vue'
-import { useRoute } from '../../src'
+import { useRoute } from 'vue-router'
 
 export default defineComponent({
   name: 'NotFound',
similarity index 95%
rename from playground/views/RepeatedParams.vue
rename to packages/playground/src/views/RepeatedParams.vue
index cafdb2d51b16b8ff8f9c39da3c2c49f2bbc3e23a..239d2f074fce4c9c58286128465fe43c28df0795 100644 (file)
@@ -11,7 +11,7 @@
 
 <script>
 import { defineComponent, computed } from 'vue'
-import { useRoute } from '../../src'
+import { useRoute } from 'vue-router'
 
 export default defineComponent({
   name: 'RepeatedParams',
diff --git a/packages/playground/tsconfig.config.json b/packages/playground/tsconfig.config.json
new file mode 100644 (file)
index 0000000..c2d3a30
--- /dev/null
@@ -0,0 +1,8 @@
+{
+  "extends": "@vue/tsconfig/tsconfig.node.json",
+  "include": ["vite.config.*", "vitest.config.*", "cypress.config.*"],
+  "compilerOptions": {
+    "composite": true,
+    "types": ["node"]
+  }
+}
diff --git a/packages/playground/tsconfig.json b/packages/playground/tsconfig.json
new file mode 100644 (file)
index 0000000..b30e9f0
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "extends": "@vue/tsconfig/tsconfig.web.json",
+  "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
+  "exclude": ["**/node_modules", "**/.*/"],
+  "compilerOptions": {
+    "baseUrl": ".",
+    "paths": {
+      "@/*": ["./src/*"]
+      // "vue-router": ["../router/src"]
+    }
+  },
+
+  "references": [
+    {
+      "path": "./tsconfig.config.json"
+    }
+  ]
+}
similarity index 50%
rename from playground/vite.config.js
rename to packages/playground/vite.config.ts
index e9cf8c637dc40729a4bcb0fedfb678e1da8b87ae..b47ace82c3160d34bc0f1b35d0d79ed8ce047316 100644 (file)
@@ -1,18 +1,16 @@
+import { fileURLToPath, URL } from 'url'
+
 import { defineConfig } from 'vite'
-import vue from '@vitejs/plugin-vue'
-import analyze from 'rollup-plugin-analyzer'
-import { resolve } from 'path'
+import Vue from '@vitejs/plugin-vue'
 
 // https://vitejs.dev/config/
 export default defineConfig({
-  root: resolve(process.cwd(), 'playground'),
-  build: {
-    outDir: '../playground_dist',
-    rollupOptions: {
-      plugins: [analyze()],
+  plugins: [Vue()],
+  resolve: {
+    alias: {
+      '@': fileURLToPath(new URL('./src', import.meta.url)),
     },
   },
-  plugins: [vue()],
   define: {
     __DEV__: JSON.stringify(!process.env.prod),
     __BROWSER__: 'true',
index 96c37e25e7ad93356f05dd2a0e9d724e33271741..b3053104b26096d1bc5d47c90ad12f1922a0fa1a 100644 (file)
     "build:e2e": "vue-tsc --noEmit && vite build --config e2e/vite.config.js",
     "build:size": "pnpm run build && rollup -c size-checks/rollup.config.js",
     "dev:e2e": "vite --config e2e/vite.config.js",
-    "docs": "vitepress dev docs",
-    "docs:build": "vitepress build docs",
-    "lint": "pnpm run lint:script && pnpm run lint:html",
-    "lint:script": "prettier -c --parser typescript \"{src,__tests__,e2e,playground}/**/*.[jt]s?(x)\"",
-    "lint:html": "prettier -c --parser html \"{playground,e2e}/**/*.html\"",
-    "lint:fix": "pnpm run lint:script --write && pnpm run lint:html --write",
     "test:types": "tsc --build tsconfig.json",
     "test:dts": "tsc -p ./test-dts/tsconfig.json",
     "test:unit": "jest --coverage",
     "@types/jest": "^27.4.1",
     "@types/jsdom": "^16.2.13",
     "@types/nightwatch": "^2.0.8",
-    "@vitejs/plugin-vue": "^2.2.2",
-    "@vue/compiler-sfc": "^3.2.31",
-    "@vue/server-renderer": "^3.2.31",
-    "@vue/test-utils": "^2.0.0-rc.3",
-    "axios": "^0.27.2",
     "browserstack-local": "^1.4.5",
     "chromedriver": "^102.0.0",
     "connect-history-api-fallback": "^1.6.0",
     "jest-mock-warn": "^1.1.0",
     "nightwatch": "^2.0.0",
     "nightwatch-helpers": "^1.2.0",
-    "prettier": "^2.4.1",
     "rimraf": "^3.0.2",
     "rollup": "^2.68.0",
     "rollup-plugin-analyzer": "^4.0.0",
     "rollup-plugin-terser": "^7.0.2",
     "rollup-plugin-typescript2": "^0.32.1",
-    "typescript": "~4.7.3",
-    "vite": "~2.9.10",
-    "vue": "^3.2.31",
-    "vue-tsc": "^0.37.2"
+    "typescript": "~4.7.2"
   }
 }
index 9e1872212d86343eddbf5db8af155efeb938ac45..bc28001f6a3f663c0b44e61ed35070a16b3a7477 100644 (file)
@@ -37,13 +37,12 @@ import { routerKey, routeLocationKey } from './injectionSymbols'
 import { RouteRecord } from './matcher/types'
 import { NavigationFailure } from './errors'
 import { isArray, isBrowser, noop } from './utils'
-import { RouterTyped } from './typedRouter'
 
 export interface RouterLinkOptions {
   /**
    * Route Location the link should navigate to when clicked on.
    */
-  to: Parameters<RouterTyped['push']>[0]
+  to: RouteLocationRaw
   /**
    * Calls `router.replace` instead of `router.push`.
    */
diff --git a/playground/tsconfig.json b/playground/tsconfig.json
deleted file mode 100644 (file)
index 7428cbb..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  "include": ["./**/*.ts", "api", "../src/global.d.ts", "shim.d.ts"],
-  "compilerOptions": {
-    "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */,
-    "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
-    // "lib": ["es2017.object"] /* Specify library files to be included in the compilation. */,
-    // "declaration": true /* Generates corresponding '.d.ts' file. */,
-    // "declarationMap": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */
-    "sourceMap": true /* Generates corresponding '.map' file. */,
-    // "outFile": "./",                       /* Concatenate and emit output to single file. */
-    "outDir": "./dist" /* Redirect output structure to the directory. */,
-
-    "strict": true /* Enable all strict type-checking options. */,
-    "noUnusedLocals": true /* Report errors on unused locals. */,
-    "noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
-
-    "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
-    "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
-  }
-}
index 8aa115ee8c8b9bf6619ad71dc67a60835578d251..4715617d238e319aa38e51880a3c95781ed93a19 100644 (file)
@@ -4,6 +4,9 @@ importers:
 
   .:
     specifiers:
+      '@vue/compiler-sfc': ^3.2.31
+      '@vue/server-renderer': ^3.2.37
+      '@vue/test-utils': ^2.0.0-rc.3
       brotli: ^1.3.3
       chalk: ^4.1.2
       enquirer: ^2.3.6
@@ -12,9 +15,16 @@ importers:
       lint-staged: ^13.0.0
       minimist: ^1.2.6
       p-series: ^3.0.0
+      prettier: ^2.4.1
       semver: ^7.3.7
+      typescript: ~4.7.2
+      vue: ^3.2.37
+      vue-tsc: ^0.37.2
       yorkie: ^2.0.0
     devDependencies:
+      '@vue/compiler-sfc': 3.2.37
+      '@vue/server-renderer': 3.2.37_vue@3.2.37
+      '@vue/test-utils': 2.0.0_vue@3.2.37
       brotli: 1.3.3
       chalk: 4.1.2
       enquirer: 2.3.6
@@ -23,7 +33,11 @@ importers:
       lint-staged: 13.0.0_enquirer@2.3.6
       minimist: 1.2.6
       p-series: 3.0.0
+      prettier: 2.6.2
       semver: 7.3.7
+      typescript: 4.7.3
+      vue: 3.2.37
+      vue-tsc: 0.37.3_typescript@4.7.3
       yorkie: 2.0.0
 
   packages/docs:
@@ -32,6 +46,27 @@ importers:
     dependencies:
       vitepress: 0.20.10
 
+  packages/playground:
+    specifiers:
+      '@types/node': ^16.11.36
+      '@vitejs/plugin-vue': ^2.3.3
+      '@vue/tsconfig': ^0.1.3
+      typescript: ~4.7.2
+      vite: ^2.9.9
+      vue: ^3.2.36
+      vue-router: workspace:*
+      vue-tsc: ^0.37.2
+    dependencies:
+      vue: 3.2.37
+    devDependencies:
+      '@types/node': 16.11.39
+      '@vitejs/plugin-vue': 2.3.3_vite@2.9.10+vue@3.2.37
+      '@vue/tsconfig': 0.1.3_@types+node@16.11.39
+      typescript: 4.7.3
+      vite: 2.9.10
+      vue-router: link:../router
+      vue-tsc: 0.37.3_typescript@4.7.3
+
   packages/router:
     specifiers:
       '@microsoft/api-extractor': ^7.18.11
@@ -43,12 +78,7 @@ importers:
       '@types/jest': ^27.4.1
       '@types/jsdom': ^16.2.13
       '@types/nightwatch': ^2.0.8
-      '@vitejs/plugin-vue': ^2.2.2
-      '@vue/compiler-sfc': ^3.2.31
       '@vue/devtools-api': ^6.1.4
-      '@vue/server-renderer': ^3.2.31
-      '@vue/test-utils': ^2.0.0-rc.3
-      axios: ^0.27.2
       browserstack-local: ^1.4.5
       chromedriver: ^102.0.0
       connect-history-api-fallback: ^1.6.0
@@ -60,16 +90,12 @@ importers:
       jest-mock-warn: ^1.1.0
       nightwatch: ^2.0.0
       nightwatch-helpers: ^1.2.0
-      prettier: ^2.4.1
       rimraf: ^3.0.2
       rollup: ^2.68.0
       rollup-plugin-analyzer: ^4.0.0
       rollup-plugin-terser: ^7.0.2
       rollup-plugin-typescript2: ^0.32.1
-      typescript: ~4.7.3
-      vite: ~2.9.10
-      vue: ^3.2.31
-      vue-tsc: ^0.37.2
+      typescript: ~4.7.2
     dependencies:
       '@vue/devtools-api': 6.1.4
     devDependencies:
@@ -82,11 +108,6 @@ importers:
       '@types/jest': 27.5.2
       '@types/jsdom': 16.2.14
       '@types/nightwatch': 2.0.8
-      '@vitejs/plugin-vue': 2.3.3_vite@2.9.10+vue@3.2.37
-      '@vue/compiler-sfc': 3.2.37
-      '@vue/server-renderer': 3.2.37_vue@3.2.37
-      '@vue/test-utils': 2.0.0_vue@3.2.37
-      axios: 0.27.2
       browserstack-local: 1.5.1
       chromedriver: 102.0.0
       connect-history-api-fallback: 1.6.0
@@ -98,16 +119,12 @@ importers:
       jest-mock-warn: 1.1.0
       nightwatch: 2.1.8_3ocnie545c4b2yffuhqjjjsvb4
       nightwatch-helpers: 1.2.0
-      prettier: 2.6.2
       rimraf: 3.0.2
       rollup: 2.75.6
       rollup-plugin-analyzer: 4.0.0
       rollup-plugin-terser: 7.0.2_rollup@2.75.6
       rollup-plugin-typescript2: 0.32.1_fgms252lqu3rk7srzpqqayl4ya
       typescript: 4.7.3
-      vite: 2.9.10
-      vue: 3.2.37
-      vue-tsc: 0.37.3_typescript@4.7.3
 
 packages:
 
@@ -1149,6 +1166,10 @@ packages:
     resolution: {integrity: sha512-yxDeaQIAJlMav7fH5AQqPH1u8YIuhYJXYBzxaQ4PifsU0GDO38MSdmEDeRlIxrKbC6NbEaaEHDanWb+y30U8SQ==}
     dev: true
 
+  /@types/node/16.11.39:
+    resolution: {integrity: sha512-K0MsdV42vPwm9L6UwhIxMAOmcvH/1OoVkZyCgEtVu4Wx7sElGloy/W7kMBNe/oJ7V/jW9BVt1F6RahH6e7tPXw==}
+    dev: true
+
   /@types/node/17.0.41:
     resolution: {integrity: sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw==}
     dev: true
@@ -1351,6 +1372,17 @@ packages:
       vue: 3.2.37
     dev: true
 
+  /@vue/tsconfig/0.1.3_@types+node@16.11.39:
+    resolution: {integrity: sha512-kQVsh8yyWPvHpb8gIc9l/HIDiiVUy1amynLNpCy8p+FoCiZXCo6fQos5/097MmnNZc9AtseDsCrfkhqCrJ8Olg==}
+    peerDependencies:
+      '@types/node': '*'
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+    dependencies:
+      '@types/node': 16.11.39
+    dev: true
+
   /JSONStream/1.3.5:
     resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==}
     hasBin: true
@@ -5839,7 +5871,7 @@ packages:
     dev: true
 
   /to-fast-properties/2.0.0:
-    resolution: {integrity: sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=}
+    resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
     engines: {node: '>=4'}
 
   /to-regex-range/5.0.1: