@kaspernj/api-maker 1.0.328 → 1.0.330

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 CHANGED
@@ -16,7 +16,7 @@
16
16
  ]
17
17
  },
18
18
  "name": "@kaspernj/api-maker",
19
- "version": "1.0.328",
19
+ "version": "1.0.330",
20
20
  "type": "module",
21
21
  "description": "",
22
22
  "main": "index.js",
@@ -11,4 +11,5 @@ export default class ApiMakerBaseModelReflection {
11
11
  macro = () => digg(this, "reflectionData", "macro")
12
12
  modelClass = () => modelClassRequire(inflection.singularize(inflection.camelize(digg(this, "reflectionData", "resource_name"))))
13
13
  name = () => inflection.camelize(digg(this, "reflectionData", "name"), true)
14
+ through = () => digg(this, "reflectionData", "through")
14
15
  }
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
- class ApiMakerRouter extends React.Component {
8
- static propTypes = {
9
- notFoundComponent: PropTypes.elementType,
10
- requireComponent: PropTypes.func.isRequired
11
- }
12
-
13
- shouldComponentUpdate(nextProps, nextState) {
14
- return shouldComponentUpdate(this, nextProps, nextState)
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
- render() {
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
- return (
26
- <Suspense fallback={<div />}>
27
- <NotFoundComponent match={match} />
28
- </Suspense>
29
- )
30
- } else {
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
- return (
38
- <Suspense fallback={<div />}>
39
- <Component match={match} {...restProps} />
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))
@@ -43,21 +43,37 @@ const ApiMakerSuperAdminShowPage = ({modelClass}) => {
43
43
  }
44
44
 
45
45
  for (const reflection of modelClass.reflections()) {
46
- if (reflection.macro() != "belongs_to") continue
47
-
48
- const reflectionModelClass = reflection.modelClass()
49
- const reflectionModelClassName = reflectionModelClass.modelClassData().name
50
- const reflectionModelClassAttributes = reflectionModelClass.attributes()
51
- const nameAttribute = reflectionModelClassAttributes.find((attribute) => attribute.name() == "name")
52
-
53
- preload.push(inflection.underscore(reflection.name()))
54
-
55
- if (!(reflectionModelClassName in select)) select[reflectionModelClassName] = []
56
- if (!select[reflectionModelClassName].includes("id")) select[reflectionModelClassName].push("id")
57
- if (nameAttribute && !select[reflectionModelClassName].includes("name")) select[reflectionModelClassName].push("name")
58
-
59
- // The foreign key is needed to look up any belongs-to-relationships
60
- if (!modelClassSelect.includes(reflection.foreignKey())) modelClassSelect.push(reflection.foreignKey())
46
+ if (reflection.macro() == "belongs_to") {
47
+ const reflectionModelClass = reflection.modelClass()
48
+ const reflectionModelClassName = reflectionModelClass.modelClassData().name
49
+ const reflectionModelClassAttributes = reflectionModelClass.attributes()
50
+ const nameAttribute = reflectionModelClassAttributes.find((attribute) => attribute.name() == "name")
51
+
52
+ preload.push(inflection.underscore(reflection.name()))
53
+
54
+ if (!(reflectionModelClassName in select)) select[reflectionModelClassName] = []
55
+ if (!select[reflectionModelClassName].includes("id")) select[reflectionModelClassName].push("id")
56
+ if (nameAttribute && !select[reflectionModelClassName].includes("name")) select[reflectionModelClassName].push("name")
57
+
58
+ // The foreign key is needed to look up any belongs-to-relationships
59
+ if (!modelClassSelect.includes(reflection.foreignKey())) modelClassSelect.push(reflection.foreignKey())
60
+ } else if (reflection.macro() == "has_one") {
61
+ const reflectionModelClass = reflection.modelClass()
62
+ const reflectionModelClassName = reflectionModelClass.modelClassData().name
63
+ const reflectionModelClassAttributes = reflectionModelClass.attributes()
64
+ const nameAttribute = reflectionModelClassAttributes.find((attribute) => attribute.name() == "name")
65
+
66
+ preload.push(inflection.underscore(reflection.name()))
67
+
68
+ if (!(reflectionModelClassName in select)) select[reflectionModelClassName] = []
69
+ if (!select[reflectionModelClassName].includes("id")) select[reflectionModelClassName].push("id")
70
+ if (nameAttribute && !select[reflectionModelClassName].includes("name")) select[reflectionModelClassName].push("name")
71
+
72
+ // The foreign key is needed to look up any has-one-relationships
73
+ if (!modelClassSelect.includes(reflection.foreignKey()) && !select[reflectionModelClassName].includes(reflection.foreignKey()) && !reflection.through()) {
74
+ select[reflectionModelClassName].push(reflection.foreignKey())
75
+ }
76
+ }
61
77
  }
62
78
 
63
79
  const useModelResult = useModel(modelClass, {
@@ -79,7 +95,7 @@ const ApiMakerSuperAdminShowPage = ({modelClass}) => {
79
95
  {attributes && model && attributes.map((attribute) =>
80
96
  <AttributePresenter attribute={attribute} key={attribute.attribute || attribute} modelArgs={modelArgs} model={model} />
81
97
  )}
82
- {model && modelClass.reflections().filter((reflection) => reflection.macro() == "belongs_to").map((reflection) =>
98
+ {model && modelClass.reflections().filter((reflection) => reflection.macro() == "belongs_to" || reflection.macro() == "has_one").map((reflection) =>
83
99
  <BelongsToAttributeRow key={reflection.name()} model={model} modelClass={modelClass} reflection={reflection} />
84
100
  )}
85
101
  {model && extraContent && extraContent(modelArgs)}
@@ -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
@@ -1,133 +1,10 @@
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 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) => class WithRouter extends React.Component {
9
- static propTypes = {
10
- path: PropTypes.string,
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
- parsedRouteDefinitions = this.parseRouteDefinitions()
16
-
17
- shouldComponentUpdate(nextProps, nextState) {
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
  }