@kaspernj/api-maker 1.0.329 → 1.0.331
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/devise.mjs +12 -0
- package/src/router.jsx +28 -35
- package/src/use-current-user.mjs +47 -7
- package/src/use-router.jsx +125 -0
- package/src/with-router.jsx +7 -130
package/package.json
CHANGED
package/src/devise.mjs
CHANGED
|
@@ -104,7 +104,19 @@ export default class ApiMakerDevise {
|
|
|
104
104
|
return this.currents[scope]
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
hasCurrentScope(scope) {
|
|
108
|
+
if (globalThis.apiMakerDeviseCurrent && scope in globalThis.apiMakerDeviseCurrent) {
|
|
109
|
+
return true
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return false
|
|
113
|
+
}
|
|
114
|
+
|
|
107
115
|
loadCurrentScope (scope) {
|
|
116
|
+
if (!this.hasCurrentScope(scope)) {
|
|
117
|
+
return null
|
|
118
|
+
}
|
|
119
|
+
|
|
108
120
|
const scopeData = globalThis.apiMakerDeviseCurrent[scope]
|
|
109
121
|
|
|
110
122
|
if (!scopeData) return null
|
package/src/router.jsx
CHANGED
|
@@ -1,45 +1,38 @@
|
|
|
1
1
|
import PropTypes from "prop-types"
|
|
2
|
-
import React from "react"
|
|
3
|
-
import shouldComponentUpdate from "set-state-compare/src/should-component-update"
|
|
2
|
+
import React, {memo} from "react"
|
|
4
3
|
import {Suspense} from "react"
|
|
5
4
|
import withRouter from "./with-router"
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
const ApiMakerRouter = (props) => {
|
|
7
|
+
const {match, ...restProps} = props
|
|
8
|
+
const {matchingRoute} = match
|
|
9
|
+
|
|
10
|
+
if (!matchingRoute) {
|
|
11
|
+
if (props.notFoundComponent) {
|
|
12
|
+
const NotFoundComponent = props.notFoundComponent
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<Suspense fallback={<div />}>
|
|
16
|
+
<NotFoundComponent match={match} />
|
|
17
|
+
</Suspense>
|
|
18
|
+
)
|
|
19
|
+
} else {
|
|
20
|
+
return null
|
|
21
|
+
}
|
|
15
22
|
}
|
|
16
23
|
|
|
17
|
-
|
|
18
|
-
const {match, ...restProps} = this.props
|
|
19
|
-
const {matchingRoute} = match
|
|
20
|
-
|
|
21
|
-
if (!matchingRoute) {
|
|
22
|
-
if (this.props.notFoundComponent) {
|
|
23
|
-
const NotFoundComponent = this.props.notFoundComponent
|
|
24
|
+
const Component = props.requireComponent({routeDefinition: matchingRoute.parsedRouteDefinition.routeDefinition})
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return null
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const Component = this.props.requireComponent({routeDefinition: matchingRoute.parsedRouteDefinition.routeDefinition})
|
|
26
|
+
return (
|
|
27
|
+
<Suspense fallback={<div />}>
|
|
28
|
+
<Component match={match} {...restProps} />
|
|
29
|
+
</Suspense>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
36
32
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
</Suspense>
|
|
41
|
-
)
|
|
42
|
-
}
|
|
33
|
+
ApiMakerRouter.propTypes = {
|
|
34
|
+
notFoundComponent: PropTypes.elementType,
|
|
35
|
+
requireComponent: PropTypes.func.isRequired
|
|
43
36
|
}
|
|
44
37
|
|
|
45
|
-
export default withRouter(ApiMakerRouter)
|
|
38
|
+
export default withRouter(memo(ApiMakerRouter))
|
package/src/use-current-user.mjs
CHANGED
|
@@ -1,10 +1,50 @@
|
|
|
1
|
-
import {useCallback, useEffect,
|
|
1
|
+
import {useCallback, useEffect, useMemo} from "react"
|
|
2
|
+
import {camelize} from "inflection"
|
|
2
3
|
import Devise from "./devise.mjs"
|
|
4
|
+
import Services from "./services.mjs"
|
|
5
|
+
import useShape from "set-state-compare/src/use-shape.js"
|
|
6
|
+
|
|
7
|
+
const currentUserData = {}
|
|
8
|
+
|
|
9
|
+
const useCurrentUser = (args) => {
|
|
10
|
+
const s = useShape()
|
|
11
|
+
const scope = args?.scope || "user"
|
|
12
|
+
|
|
13
|
+
s.meta.scope = scope
|
|
14
|
+
s.meta.scopeName = useMemo(() => `current${camelize(s.m.scope)}`, [scope])
|
|
15
|
+
|
|
16
|
+
const loadCurrentUserFromRequest = useCallback(async () => {
|
|
17
|
+
const {scope, scopeName} = s.m.scope
|
|
18
|
+
const result = await Services.current().sendRequest("Devise::Current", {scope})
|
|
19
|
+
const current = digg(result, "current")
|
|
20
|
+
|
|
21
|
+
currentUserData[scope] = current
|
|
22
|
+
|
|
23
|
+
s.setStates[scopeName](current)
|
|
24
|
+
}, [])
|
|
25
|
+
|
|
26
|
+
const defaultCurrentUser = useCallback(() => {
|
|
27
|
+
const {scope, scopeName} = s.m
|
|
28
|
+
|
|
29
|
+
if (scope in currentUserData) {
|
|
30
|
+
return currentUserData[scope]
|
|
31
|
+
} else if (Devise.current().hasCurrentScope(scope)) {
|
|
32
|
+
currentUserData[scope] = Devise[scopeName]()
|
|
33
|
+
|
|
34
|
+
return currentUserData[scope]
|
|
35
|
+
}
|
|
36
|
+
}, [])
|
|
37
|
+
|
|
38
|
+
s.useStates({currentUser: defaultCurrentUser()})
|
|
3
39
|
|
|
4
|
-
const useCurrentUser = () => {
|
|
5
|
-
const [currentUser, setCurrentUser] = useState(Devise.currentUser())
|
|
6
40
|
const updateCurrentUser = useCallback(() => {
|
|
7
|
-
|
|
41
|
+
s.set({currentUser: Devise[s.m.scopeName]()})
|
|
42
|
+
}, [])
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!(s.m.scope in currentUserData)) {
|
|
46
|
+
loadCurrentUserFromRequest()
|
|
47
|
+
}
|
|
8
48
|
}, [])
|
|
9
49
|
|
|
10
50
|
useEffect(() => {
|
|
@@ -13,7 +53,7 @@ const useCurrentUser = () => {
|
|
|
13
53
|
return () => {
|
|
14
54
|
Devise.events().removeListener("onDeviseSignIn", updateCurrentUser)
|
|
15
55
|
}
|
|
16
|
-
})
|
|
56
|
+
}, [])
|
|
17
57
|
|
|
18
58
|
useEffect(() => {
|
|
19
59
|
Devise.events().addListener("onDeviseSignOut", updateCurrentUser)
|
|
@@ -21,9 +61,9 @@ const useCurrentUser = () => {
|
|
|
21
61
|
return () => {
|
|
22
62
|
Devise.events().removeListener("onDeviseSignOut", updateCurrentUser)
|
|
23
63
|
}
|
|
24
|
-
})
|
|
64
|
+
}, [])
|
|
25
65
|
|
|
26
|
-
return currentUser
|
|
66
|
+
return s.s.currentUser
|
|
27
67
|
}
|
|
28
68
|
|
|
29
69
|
export default useCurrentUser
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import config from "./config.mjs"
|
|
2
|
+
import escapeStringRegexp from "escape-string-regexp"
|
|
3
|
+
import * as inflection from "inflection"
|
|
4
|
+
import PropTypes from "prop-types"
|
|
5
|
+
import {useCallback, useMemo} from "react"
|
|
6
|
+
import useShape from "set-state-compare/src/use-shape.js"
|
|
7
|
+
|
|
8
|
+
const useRouter = (props) => {
|
|
9
|
+
const s = useShape(props)
|
|
10
|
+
|
|
11
|
+
const findRouteParams = useCallback((routeDefinition) => {
|
|
12
|
+
const result = []
|
|
13
|
+
const parts = routeDefinition.path.split("/")
|
|
14
|
+
|
|
15
|
+
for (const part of parts) {
|
|
16
|
+
if (part.match(/^:([a-z_]+)$/))
|
|
17
|
+
result.push(part)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return result
|
|
21
|
+
}, [])
|
|
22
|
+
|
|
23
|
+
const getPath = useCallback(() => {
|
|
24
|
+
let path = s.p.path || window.location.pathname
|
|
25
|
+
|
|
26
|
+
path = path.replace(/[/]+$/, "")
|
|
27
|
+
|
|
28
|
+
return path
|
|
29
|
+
}, [])
|
|
30
|
+
|
|
31
|
+
const getRouteDefinitions = useCallback(() => s.p.routeDefinitions || config.getRouteDefinitions(), [])
|
|
32
|
+
const getRoutes = useCallback(() => s.p.routes || config.getRoutes(), [])
|
|
33
|
+
|
|
34
|
+
const parseRouteDefinitions = useCallback(() => {
|
|
35
|
+
const Locales = require("shared/locales").default
|
|
36
|
+
const routeDefinitions = getRouteDefinitions()
|
|
37
|
+
const routes = getRoutes()
|
|
38
|
+
const regex = /:([A-z\d_]+)/
|
|
39
|
+
const parsedRouteDefinitions = []
|
|
40
|
+
|
|
41
|
+
for (const locale of Locales.availableLocales()) {
|
|
42
|
+
for (const routeDefinition of routeDefinitions.routes) {
|
|
43
|
+
const routePathName = `${inflection.camelize(routeDefinition.name, true)}Path`
|
|
44
|
+
const params = findRouteParams(routeDefinition)
|
|
45
|
+
|
|
46
|
+
params.push({locale})
|
|
47
|
+
|
|
48
|
+
if (!(routePathName in routes))
|
|
49
|
+
throw new Error(`${routePathName} not found in routes: ${Object.keys(routes, ", ")}`)
|
|
50
|
+
|
|
51
|
+
const routePath = routes[routePathName](...params).replace(/[/]+$/, "")
|
|
52
|
+
const groups = []
|
|
53
|
+
let pathRegexString = "^"
|
|
54
|
+
|
|
55
|
+
pathRegexString += escapeStringRegexp(routePath)
|
|
56
|
+
|
|
57
|
+
while (true) {
|
|
58
|
+
const match = pathRegexString.match(regex)
|
|
59
|
+
|
|
60
|
+
if (!match) break
|
|
61
|
+
|
|
62
|
+
const variableName = match[1]
|
|
63
|
+
|
|
64
|
+
groups.push(variableName)
|
|
65
|
+
|
|
66
|
+
pathRegexString = pathRegexString.replace(match[0], "([^/]+)")
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
pathRegexString += "$"
|
|
70
|
+
|
|
71
|
+
const pathRegex = new RegExp(pathRegexString)
|
|
72
|
+
|
|
73
|
+
parsedRouteDefinitions.push({groups, pathRegex, routeDefinition})
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return parsedRouteDefinitions
|
|
78
|
+
}, [])
|
|
79
|
+
|
|
80
|
+
const parsedRouteDefinitions = useMemo(() => parseRouteDefinitions(), [])
|
|
81
|
+
|
|
82
|
+
s.updateMeta({parsedRouteDefinitions})
|
|
83
|
+
|
|
84
|
+
const findMatchingRoute = useCallback(() => {
|
|
85
|
+
const path = getPath()
|
|
86
|
+
|
|
87
|
+
for (const parsedRouteDefinition of s.m.parsedRouteDefinitions) {
|
|
88
|
+
const match = path.match(parsedRouteDefinition.pathRegex)
|
|
89
|
+
let matched, params
|
|
90
|
+
|
|
91
|
+
if (match) {
|
|
92
|
+
matched = true
|
|
93
|
+
params = {}
|
|
94
|
+
|
|
95
|
+
for (const groupKey in parsedRouteDefinition.groups) {
|
|
96
|
+
const groupName = parsedRouteDefinition.groups[groupKey]
|
|
97
|
+
|
|
98
|
+
params[groupName] = match[Number(groupKey) + 1]
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (path == "" && parsedRouteDefinition.routeDefinition.path == "/") matched = true
|
|
103
|
+
if (matched) {
|
|
104
|
+
return {params, parsedRouteDefinition}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}, [])
|
|
108
|
+
|
|
109
|
+
const matchingRoute = findMatchingRoute()
|
|
110
|
+
const params = matchingRoute?.params || {}
|
|
111
|
+
const match = {
|
|
112
|
+
matchingRoute,
|
|
113
|
+
params
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {match}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
useRouter.propTypes = {
|
|
120
|
+
path: PropTypes.string,
|
|
121
|
+
routeDefinitions: PropTypes.object,
|
|
122
|
+
routes: PropTypes.object
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export default useRouter
|
package/src/with-router.jsx
CHANGED
|
@@ -1,133 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
import escapeStringRegexp from "escape-string-regexp"
|
|
3
|
-
import * as inflection from "inflection"
|
|
4
|
-
import PropTypes from "prop-types"
|
|
5
|
-
import React from "react"
|
|
6
|
-
import shouldComponentUpdate from "set-state-compare/src/should-component-update"
|
|
1
|
+
import useRouter from "./use-router"
|
|
7
2
|
|
|
8
|
-
export default (WrapperComponent) =>
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
routeDefinitions: PropTypes.object,
|
|
12
|
-
routes: PropTypes.object
|
|
13
|
-
}
|
|
3
|
+
export default (WrapperComponent) => (props) => {
|
|
4
|
+
const {path, routes, routeDefinitions, ...restProps} = props
|
|
5
|
+
const {match} = useRouter({path, routes, routeDefinitions})
|
|
14
6
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return shouldComponentUpdate(this, nextProps, nextState)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
findRouteParams(routeDefinition) {
|
|
22
|
-
const result = []
|
|
23
|
-
const parts = routeDefinition.path.split("/")
|
|
24
|
-
|
|
25
|
-
for (const part of parts) {
|
|
26
|
-
if (part.match(/^:([a-z_]+)$/))
|
|
27
|
-
result.push(part)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return result
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
path() {
|
|
34
|
-
let path = this.props.path || window.location.pathname
|
|
35
|
-
|
|
36
|
-
path = path.replace(/[/]+$/, "")
|
|
37
|
-
|
|
38
|
-
return path
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
routeDefinitions() {
|
|
42
|
-
return this.props.routeDefinitions || config.getRouteDefinitions()
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
routes() {
|
|
46
|
-
return this.props.routes || config.getRoutes()
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
parseRouteDefinitions() {
|
|
50
|
-
const Locales = require("shared/locales").default
|
|
51
|
-
const routeDefinitions = this.routeDefinitions()
|
|
52
|
-
const routes = this.routes()
|
|
53
|
-
const regex = /:([A-z\d_]+)/
|
|
54
|
-
const parsedRouteDefinitions = []
|
|
55
|
-
|
|
56
|
-
for (const locale of Locales.availableLocales()) {
|
|
57
|
-
for (const routeDefinition of routeDefinitions.routes) {
|
|
58
|
-
const routePathName = `${inflection.camelize(routeDefinition.name, true)}Path`
|
|
59
|
-
const params = this.findRouteParams(routeDefinition)
|
|
60
|
-
|
|
61
|
-
params.push({locale})
|
|
62
|
-
|
|
63
|
-
if (!(routePathName in routes))
|
|
64
|
-
throw new Error(`${routePathName} not found in routes: ${Object.keys(routes, ", ")}`)
|
|
65
|
-
|
|
66
|
-
const routePath = routes[routePathName](...params).replace(/[/]+$/, "")
|
|
67
|
-
const groups = []
|
|
68
|
-
let pathRegexString = "^"
|
|
69
|
-
|
|
70
|
-
pathRegexString += escapeStringRegexp(routePath)
|
|
71
|
-
|
|
72
|
-
while (true) {
|
|
73
|
-
const match = pathRegexString.match(regex)
|
|
74
|
-
|
|
75
|
-
if (!match) break
|
|
76
|
-
|
|
77
|
-
const variableName = match[1]
|
|
78
|
-
|
|
79
|
-
groups.push(variableName)
|
|
80
|
-
|
|
81
|
-
pathRegexString = pathRegexString.replace(match[0], "([^/]+)")
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
pathRegexString += "$"
|
|
85
|
-
|
|
86
|
-
const pathRegex = new RegExp(pathRegexString)
|
|
87
|
-
|
|
88
|
-
parsedRouteDefinitions.push({groups, pathRegex, routeDefinition})
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return parsedRouteDefinitions
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
findMatchingRoute() {
|
|
96
|
-
const path = this.path()
|
|
97
|
-
|
|
98
|
-
for (const parsedRouteDefinition of this.parsedRouteDefinitions) {
|
|
99
|
-
const match = path.match(parsedRouteDefinition.pathRegex)
|
|
100
|
-
let matched, params
|
|
101
|
-
|
|
102
|
-
if (match) {
|
|
103
|
-
matched = true
|
|
104
|
-
params = {}
|
|
105
|
-
|
|
106
|
-
for (const groupKey in parsedRouteDefinition.groups) {
|
|
107
|
-
const groupName = parsedRouteDefinition.groups[groupKey]
|
|
108
|
-
|
|
109
|
-
params[groupName] = match[Number(groupKey) + 1]
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (path == "" && parsedRouteDefinition.routeDefinition.path == "/") matched = true
|
|
114
|
-
if (matched) {
|
|
115
|
-
return {params, parsedRouteDefinition}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
render() {
|
|
121
|
-
const {path, routes, routeDefinitions, ...restProps} = this.props
|
|
122
|
-
const matchingRoute = this.findMatchingRoute()
|
|
123
|
-
const params = matchingRoute?.params || {}
|
|
124
|
-
const match = {
|
|
125
|
-
matchingRoute,
|
|
126
|
-
params
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return (
|
|
130
|
-
<WrapperComponent match={match} {...restProps} />
|
|
131
|
-
)
|
|
132
|
-
}
|
|
7
|
+
return (
|
|
8
|
+
<WrapperComponent match={match} {...restProps} />
|
|
9
|
+
)
|
|
133
10
|
}
|