@appsemble/utils 0.40.3-test.4 → 0.40.4
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/README.md +3 -3
- package/appMessages.js +2 -1
- package/package.json +3 -3
- package/validateAppMessages.d.ts +14 -0
- package/validateAppMessages.js +63 -1
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
#  Appsemble Utilities
|
|
2
2
|
|
|
3
3
|
> Internal utility functions used across multiple Appsemble projects.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@appsemble/utils)
|
|
6
|
-
[](https://gitlab.com/appsemble/appsemble/-/releases/0.40.4)
|
|
7
7
|
[](https://prettier.io)
|
|
8
8
|
|
|
9
9
|
## Table of Contents
|
|
@@ -26,5 +26,5 @@ not guaranteed.
|
|
|
26
26
|
|
|
27
27
|
## License
|
|
28
28
|
|
|
29
|
-
[LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.40.
|
|
29
|
+
[LGPL-3.0-only](https://gitlab.com/appsemble/appsemble/-/blob/0.40.4/LICENSE.md) ©
|
|
30
30
|
[Appsemble](https://appsemble.com)
|
package/appMessages.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { iterApp, } from '@appsemble/lang-sdk';
|
|
1
|
+
import { getRouteMessageId, iterApp, translatableRoutes, } from '@appsemble/lang-sdk';
|
|
2
2
|
import { normalize } from './normalize.js';
|
|
3
3
|
/**
|
|
4
4
|
* Recursively find `string.format` remapper message IDs.
|
|
@@ -43,6 +43,7 @@ export function extractAppMessages(app, onBlock) {
|
|
|
43
43
|
[`app.roles.${role}`, role],
|
|
44
44
|
[`app.roles.${role}.description`, roleDefinition.description],
|
|
45
45
|
])),
|
|
46
|
+
...Object.fromEntries(translatableRoutes.map((name) => [getRouteMessageId(name), name])),
|
|
46
47
|
},
|
|
47
48
|
messageIds: {},
|
|
48
49
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appsemble/utils",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.4",
|
|
4
4
|
"description": "Utility functions used in Appsemble internally",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"app",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"test": "vitest"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@appsemble/lang-sdk": "0.40.
|
|
42
|
-
"@appsemble/types": "0.40.
|
|
41
|
+
"@appsemble/lang-sdk": "0.40.4",
|
|
42
|
+
"@appsemble/types": "0.40.4",
|
|
43
43
|
"@fortawesome/fontawesome-common-types": "^6.0.0",
|
|
44
44
|
"axios": "^1.0.0",
|
|
45
45
|
"langmap": "^0.0.16",
|
package/validateAppMessages.d.ts
CHANGED
|
@@ -4,3 +4,17 @@ export declare class AppMessageValidationError extends Error {
|
|
|
4
4
|
constructor(message: string);
|
|
5
5
|
}
|
|
6
6
|
export declare function validateMessages(messages: AppsembleMessages, app: AppDefinition): void;
|
|
7
|
+
/**
|
|
8
|
+
* Validate the URL segments an app serves in a language.
|
|
9
|
+
*
|
|
10
|
+
* The app messages of the language are the extracted defaults of the definition, overlaid by each
|
|
11
|
+
* layer in order. An empty string in a layer keeps the value below it. A page's segment is its
|
|
12
|
+
* normalized translated name, or the normalized page name when the translated name has no Latin
|
|
13
|
+
* letters or digits. A route's segment is its `routes.<Name>` message as written.
|
|
14
|
+
*
|
|
15
|
+
* @param app The app definition the messages belong to.
|
|
16
|
+
* @param language The language the layers are served as, used in the error message.
|
|
17
|
+
* @param layers The stored messages that make up the language, lowest layer first: the base
|
|
18
|
+
* language row, then the regional row.
|
|
19
|
+
*/
|
|
20
|
+
export declare function validateMessageSlugs(app: AppDefinition, language: string, layers: Partial<AppsembleMessages>[]): void;
|
package/validateAppMessages.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { normalizeBlockName } from '@appsemble/lang-sdk';
|
|
1
|
+
import { getPageMessageId, getRouteMessageId, normalizeBlockName, translatableRoutes, } from '@appsemble/lang-sdk';
|
|
2
2
|
import { extractAppMessages } from './appMessages.js';
|
|
3
3
|
import { has } from './miscellaneous.js';
|
|
4
|
+
import { normalize } from './normalize.js';
|
|
5
|
+
const routeSegmentPattern = /^[A-Z][\dA-Za-z-]*$/;
|
|
4
6
|
export class AppMessageValidationError extends Error {
|
|
5
7
|
constructor(message) {
|
|
6
8
|
super(message);
|
|
@@ -70,4 +72,64 @@ This block version is not used in the app`);
|
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
74
|
}
|
|
75
|
+
function collectPages(pages) {
|
|
76
|
+
return pages.flatMap((page) => page.type === 'container' ? [page, ...collectPages(page.pages)] : [page]);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Validate the URL segments an app serves in a language.
|
|
80
|
+
*
|
|
81
|
+
* The app messages of the language are the extracted defaults of the definition, overlaid by each
|
|
82
|
+
* layer in order. An empty string in a layer keeps the value below it. A page's segment is its
|
|
83
|
+
* normalized translated name, or the normalized page name when the translated name has no Latin
|
|
84
|
+
* letters or digits. A route's segment is its `routes.<Name>` message as written.
|
|
85
|
+
*
|
|
86
|
+
* @param app The app definition the messages belong to.
|
|
87
|
+
* @param language The language the layers are served as, used in the error message.
|
|
88
|
+
* @param layers The stored messages that make up the language, lowest layer first: the base
|
|
89
|
+
* language row, then the regional row.
|
|
90
|
+
*/
|
|
91
|
+
export function validateMessageSlugs(app, language, layers) {
|
|
92
|
+
const appMessages = { ...extractAppMessages(app).app };
|
|
93
|
+
for (const layer of layers) {
|
|
94
|
+
for (const [key, value] of Object.entries(layer.app ?? {})) {
|
|
95
|
+
if (typeof value === 'string' && value) {
|
|
96
|
+
appMessages[key] = value;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const pages = collectPages(app.pages ?? []);
|
|
101
|
+
const canonicalSegments = new Map(pages.map((page) => [normalize(page.name), page]));
|
|
102
|
+
const translatedSegments = new Map();
|
|
103
|
+
for (const page of pages) {
|
|
104
|
+
const canonical = normalize(page.name);
|
|
105
|
+
// A translated name without Latin letters or digits keeps the canonical segment.
|
|
106
|
+
const segment = normalize(appMessages[getPageMessageId(page.name)]) || canonical;
|
|
107
|
+
if (segment !== canonical) {
|
|
108
|
+
if (segment === 'debug') {
|
|
109
|
+
throw new AppMessageValidationError(`Language “${language}”: the translated name of page “${page.name}” has the reserved URL segment “debug”`);
|
|
110
|
+
}
|
|
111
|
+
const other = translatedSegments.get(segment) ?? canonicalSegments.get(segment);
|
|
112
|
+
if (other) {
|
|
113
|
+
throw new AppMessageValidationError(`Language “${language}”: pages “${other.name}” and “${page.name}” share the URL segment “${segment}”`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
translatedSegments.set(segment, page);
|
|
117
|
+
}
|
|
118
|
+
const routeSegments = translatableRoutes.map((name) => [name, appMessages[getRouteMessageId(name)]]);
|
|
119
|
+
for (const [name, segment] of routeSegments) {
|
|
120
|
+
if (segment === name) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (!routeSegmentPattern.test(segment)) {
|
|
124
|
+
throw new AppMessageValidationError(`Language “${language}”: the URL segment “${segment}” of route “${name}” must start with an uppercase letter and may only contain letters, digits and hyphens`);
|
|
125
|
+
}
|
|
126
|
+
if (segment === 'Callback' || segment.toLowerCase() === 'debug') {
|
|
127
|
+
throw new AppMessageValidationError(`Language “${language}”: the URL segment “${segment}” of route “${name}” is reserved`);
|
|
128
|
+
}
|
|
129
|
+
const other = routeSegments.find(([otherName, otherSegment]) => otherName !== name && (segment === otherName || segment === otherSegment));
|
|
130
|
+
if (other) {
|
|
131
|
+
throw new AppMessageValidationError(`Language “${language}”: routes “${name}” and “${other[0]}” share the URL segment “${segment}”`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
73
135
|
//# sourceMappingURL=validateAppMessages.js.map
|