@aidc-toolkit/app-extension 0.9.19-beta → 0.9.20-beta

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 CHANGED
@@ -14,4 +14,4 @@ Unless required by applicable law or agreed to in writing, software distributed
14
14
  >
15
15
  > **This software is in beta**, with production release is scheduled for 2025Q4. To follow the status of this and other projects, go to the AIDC Toolkit [projects](https://github.com/orgs/aidc-toolkit/projects) page.
16
16
 
17
- The AIDC Toolkit `app-extension` package is the bridge between the AIDC Toolkit and applications such s Microsoft Excel and Google Sheets. It provides little functionality of its own.
17
+ The AIDC Toolkit `app-extension` package is the bridge between the AIDC Toolkit and applications such as Microsoft Excel and Google Sheets. It does so by flattening the object model, replacing stateful object construction and method calls with stateless functions.
@@ -0,0 +1,107 @@
1
+ import type { BaseParameterDescriptor, ClassDescriptor, MethodDescriptor } from "../descriptor.js";
2
+ /**
3
+ * Localization.
4
+ */
5
+ export interface Localization {
6
+ /**
7
+ * Name.
8
+ */
9
+ name: string;
10
+ /**
11
+ * Description.
12
+ */
13
+ description: string;
14
+ }
15
+ /**
16
+ * Function localization.
17
+ */
18
+ export interface FunctionLocalization extends Localization {
19
+ /**
20
+ * Documentation URL.
21
+ */
22
+ documentationURL: string;
23
+ }
24
+ /**
25
+ * Parameter localization.
26
+ */
27
+ export interface ParameterLocalization extends Localization {
28
+ }
29
+ /**
30
+ * Localization descriptor.
31
+ */
32
+ export interface LocalizationDescriptor<T extends Localization> {
33
+ /**
34
+ * Localizations map by locale.
35
+ */
36
+ readonly localizationsMap: ReadonlyMap<string, T>;
37
+ }
38
+ /**
39
+ * Proxy namespace descriptor.
40
+ */
41
+ export interface ProxyNamespaceDescriptor {
42
+ /**
43
+ * Namespace if any.
44
+ */
45
+ readonly namespace: string | undefined;
46
+ }
47
+ /**
48
+ * Proxy class descriptor.
49
+ */
50
+ export interface ProxyClassDescriptor extends ProxyNamespaceDescriptor {
51
+ /**
52
+ * Class name.
53
+ */
54
+ readonly className: string;
55
+ /**
56
+ * Namespace-qualified class name.
57
+ */
58
+ readonly namespaceClassName: string;
59
+ /**
60
+ * Class descriptor.
61
+ */
62
+ readonly classDescriptor: ClassDescriptor;
63
+ }
64
+ /**
65
+ * Proxy object descriptor.
66
+ */
67
+ export interface ProxyObjectDescriptor extends ProxyClassDescriptor {
68
+ /**
69
+ * Object name.
70
+ */
71
+ readonly objectName: string;
72
+ }
73
+ /**
74
+ * Proxy parameter descriptor.
75
+ */
76
+ export interface ProxyParameterDescriptor extends ProxyNamespaceDescriptor, LocalizationDescriptor<ParameterLocalization> {
77
+ /**
78
+ * Function name.
79
+ */
80
+ readonly parameterName: string;
81
+ /**
82
+ * Parameter descriptor.
83
+ */
84
+ readonly parameterDescriptor: BaseParameterDescriptor;
85
+ }
86
+ /**
87
+ * Proxy function descriptor.
88
+ */
89
+ export interface ProxyFunctionDescriptor extends ProxyObjectDescriptor, LocalizationDescriptor<FunctionLocalization> {
90
+ /**
91
+ * Function name.
92
+ */
93
+ readonly functionName: string;
94
+ /**
95
+ * Namespace-qualified function name.
96
+ */
97
+ readonly namespaceFunctionName: string;
98
+ /**
99
+ * Proxy parameter descriptors
100
+ */
101
+ readonly proxyParameterDescriptors: ProxyParameterDescriptor[];
102
+ /**
103
+ * Method descriptor.
104
+ */
105
+ readonly methodDescriptor: MethodDescriptor;
106
+ }
107
+ //# sourceMappingURL=descriptor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"descriptor.d.ts","sourceRoot":"","sources":["../../src/generator/descriptor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEnG;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAqB,SAAQ,YAAY;IACtD;;OAEG;IACH,gBAAgB,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAsB,SAAQ,YAAY;CAC1D;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB,CAAC,CAAC,SAAS,YAAY;IAC1D;;OAEG;IACH,QAAQ,CAAC,gBAAgB,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;CACrD;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACrC;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C;AAED;;GAEG;AACH,MAAM,WAAW,oBAAqB,SAAQ,wBAAwB;IAClE;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B;;OAEG;IACH,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IAEpC;;OAEG;IACH,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;CAC7C;AAED;;GAEG;AACH,MAAM,WAAW,qBAAsB,SAAQ,oBAAoB;IAC/D;;OAEG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,wBAAyB,SAAQ,wBAAwB,EAAE,sBAAsB,CAAC,qBAAqB,CAAC;IACrH;;OAEG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAE/B;;OAEG;IACH,QAAQ,CAAC,mBAAmB,EAAE,uBAAuB,CAAC;CACzD;AAED;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,qBAAqB,EAAE,sBAAsB,CAAC,oBAAoB,CAAC;IAChH;;OAEG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAE9B;;OAEG;IACH,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IAEvC;;OAEG;IACH,QAAQ,CAAC,yBAAyB,EAAE,wBAAwB,EAAE,CAAC;IAE/D;;OAEG;IACH,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;CAC/C"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=descriptor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"descriptor.js","sourceRoot":"","sources":["../../src/generator/descriptor.ts"],"names":[],"mappings":""}
@@ -0,0 +1,120 @@
1
+ import type { FunctionLocalization, ParameterLocalization, ProxyFunctionDescriptor, ProxyObjectDescriptor } from "./descriptor.js";
2
+ /**
3
+ * Abstract generator.
4
+ */
5
+ export declare abstract class Generator {
6
+ /**
7
+ * Documentation base URL.
8
+ */
9
+ private static readonly DOCUMENTATION_BASE_URL;
10
+ /**
11
+ * Documentation path, optionally preceded by locale.
12
+ */
13
+ private static readonly DOCUMENTATION_PATH;
14
+ /**
15
+ * Locales.
16
+ */
17
+ private readonly _locales;
18
+ /**
19
+ * Default locale.
20
+ */
21
+ private readonly _defaultLocale;
22
+ /**
23
+ * Map of function localizations maps by namespace function name.
24
+ */
25
+ private readonly _functionLocalizationsMapsMap;
26
+ /**
27
+ * Map of parameter localizations maps by namespace function parameter name.
28
+ */
29
+ private readonly _parameterLocalizationsMapsMap;
30
+ /**
31
+ *
32
+ */
33
+ /**
34
+ * Constructor.
35
+ *
36
+ * @param includeLocalizations
37
+ * Include localizations if true.
38
+ */
39
+ constructor(includeLocalizations?: boolean);
40
+ /**
41
+ * Get the locales.
42
+ */
43
+ protected get locales(): readonly string[];
44
+ /**
45
+ * Get the default locale.
46
+ */
47
+ protected get defaultLocale(): string;
48
+ /**
49
+ * Get function localization.
50
+ *
51
+ * @param namespaceFunctionName
52
+ * Namespace function name.
53
+ *
54
+ * @param locale
55
+ * Locale.
56
+ *
57
+ * @returns
58
+ * Function localization.
59
+ */
60
+ protected getFunctionLocalization(namespaceFunctionName: string, locale: string): FunctionLocalization;
61
+ /**
62
+ * Get parameter localization.
63
+ *
64
+ * @param namespaceFunctionName
65
+ * Namespace function name.
66
+ *
67
+ * @param parameterName
68
+ * Parameter name.
69
+ *
70
+ * @param locale
71
+ * Locale.
72
+ *
73
+ * @returns
74
+ * Function localization.
75
+ */
76
+ protected getParameterLocalization(namespaceFunctionName: string, parameterName: string, locale: string): ParameterLocalization;
77
+ /**
78
+ * Initialize the generation of the output.
79
+ */
80
+ protected abstract initialize(): void;
81
+ /**
82
+ * Create a proxy object.
83
+ *
84
+ * @param proxyObjectDescriptor
85
+ * Proxy object descriptor.
86
+ */
87
+ protected abstract createProxyObject(proxyObjectDescriptor: ProxyObjectDescriptor): void;
88
+ /**
89
+ * Create a proxy function.
90
+ *
91
+ * @param proxyFunctionDescriptor
92
+ * Proxy function descriptor.
93
+ */
94
+ protected abstract createProxyFunction(proxyFunctionDescriptor: ProxyFunctionDescriptor): void;
95
+ /**
96
+ * Finalize the generation of the output.
97
+ *
98
+ * @param success
99
+ * True if successful.
100
+ */
101
+ protected abstract finalize(success: boolean): void | Promise<void>;
102
+ /**
103
+ * Generate localizations map.
104
+ *
105
+ * @param localizedKeyPrefix
106
+ * Localized key prefix.
107
+ *
108
+ * @param localizationCallback
109
+ * Callback to finalize localization.
110
+ *
111
+ * @returns
112
+ * Localization map.
113
+ */
114
+ private generateLocalizationsMap;
115
+ /**
116
+ * Generate by processing individual imports.
117
+ */
118
+ generate(): Promise<void>;
119
+ }
120
+ //# sourceMappingURL=generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/generator/generator.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACR,oBAAoB,EAEpB,qBAAqB,EACrB,uBAAuB,EACvB,qBAAqB,EACxB,MAAM,iBAAiB,CAAC;AAEzB;;GAEG;AACH,8BAAsB,SAAS;IAC3B;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAA+B;IAE7E;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAoB;IAE9D;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAE7C;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IAExC;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,6BAA6B,CAAgE;IAE9G;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,8BAA8B,CAAiE;IAEhH;;OAEG;IAEH;;;;;OAKG;gBACS,oBAAoB,UAAO;IAKvC;;OAEG;IACH,SAAS,KAAK,OAAO,IAAI,SAAS,MAAM,EAAE,CAEzC;IAED;;OAEG;IACH,SAAS,KAAK,aAAa,IAAI,MAAM,CAEpC;IAED;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,uBAAuB,CAAC,qBAAqB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,oBAAoB;IAUtG;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,wBAAwB,CAAC,qBAAqB,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,qBAAqB;IAU/H;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,UAAU,IAAI,IAAI;IAErC;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,qBAAqB,GAAG,IAAI;IAExF;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,mBAAmB,CAAC,uBAAuB,EAAE,uBAAuB,GAAG,IAAI;IAE9F;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAEnE;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,wBAAwB;IAehC;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAyGlC"}
@@ -0,0 +1,213 @@
1
+ import { I18nEnvironment } from "@aidc-toolkit/core";
2
+ import { expandParameterDescriptor, getClassDescriptors } from "../descriptor.js";
3
+ import { appExtensionResources, i18nAppExtensionInit, i18nextAppExtension } from "../locale/i18n.js";
4
+ /**
5
+ * Abstract generator.
6
+ */
7
+ export class Generator {
8
+ /**
9
+ * Documentation base URL.
10
+ */
11
+ static DOCUMENTATION_BASE_URL = "https://aidc-toolkit.com/";
12
+ /**
13
+ * Documentation path, optionally preceded by locale.
14
+ */
15
+ static DOCUMENTATION_PATH = "app-extension/";
16
+ /**
17
+ * Locales.
18
+ */
19
+ _locales;
20
+ /**
21
+ * Default locale.
22
+ */
23
+ _defaultLocale;
24
+ /**
25
+ * Map of function localizations maps by namespace function name.
26
+ */
27
+ _functionLocalizationsMapsMap = new Map();
28
+ /**
29
+ * Map of parameter localizations maps by namespace function parameter name.
30
+ */
31
+ _parameterLocalizationsMapsMap = new Map();
32
+ /**
33
+ *
34
+ */
35
+ /**
36
+ * Constructor.
37
+ *
38
+ * @param includeLocalizations
39
+ * Include localizations if true.
40
+ */
41
+ constructor(includeLocalizations = true) {
42
+ this._locales = includeLocalizations ? Object.keys(appExtensionResources) : [];
43
+ this._defaultLocale = this._locales[0] ?? "";
44
+ }
45
+ /**
46
+ * Get the locales.
47
+ */
48
+ get locales() {
49
+ return this._locales;
50
+ }
51
+ /**
52
+ * Get the default locale.
53
+ */
54
+ get defaultLocale() {
55
+ return this._defaultLocale;
56
+ }
57
+ /**
58
+ * Get function localization.
59
+ *
60
+ * @param namespaceFunctionName
61
+ * Namespace function name.
62
+ *
63
+ * @param locale
64
+ * Locale.
65
+ *
66
+ * @returns
67
+ * Function localization.
68
+ */
69
+ getFunctionLocalization(namespaceFunctionName, locale) {
70
+ const functionLocalization = this._functionLocalizationsMapsMap.get(namespaceFunctionName)?.get(locale);
71
+ if (functionLocalization === undefined) {
72
+ throw new Error(`Localization for function "${namespaceFunctionName}", locale "${locale}" not found`);
73
+ }
74
+ return functionLocalization;
75
+ }
76
+ /**
77
+ * Get parameter localization.
78
+ *
79
+ * @param namespaceFunctionName
80
+ * Namespace function name.
81
+ *
82
+ * @param parameterName
83
+ * Parameter name.
84
+ *
85
+ * @param locale
86
+ * Locale.
87
+ *
88
+ * @returns
89
+ * Function localization.
90
+ */
91
+ getParameterLocalization(namespaceFunctionName, parameterName, locale) {
92
+ const parameterLocalization = this._parameterLocalizationsMapsMap.get(`${namespaceFunctionName}.${parameterName}`)?.get(locale);
93
+ if (parameterLocalization === undefined) {
94
+ throw new Error(`Localization for function "${namespaceFunctionName}", parameter "${parameterName}", locale "${locale}" not found`);
95
+ }
96
+ return parameterLocalization;
97
+ }
98
+ /**
99
+ * Generate localizations map.
100
+ *
101
+ * @param localizedKeyPrefix
102
+ * Localized key prefix.
103
+ *
104
+ * @param localizationCallback
105
+ * Callback to finalize localization.
106
+ *
107
+ * @returns
108
+ * Localization map.
109
+ */
110
+ generateLocalizationsMap(localizedKeyPrefix, localizationCallback) {
111
+ return new Map(this._locales.map((locale) => {
112
+ const lngOption = {
113
+ lng: locale
114
+ };
115
+ return [locale, localizationCallback(locale, {
116
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Localized key exists.
117
+ name: i18nextAppExtension.t(`${localizedKeyPrefix}name`, lngOption),
118
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Localized key exists.
119
+ description: i18nextAppExtension.t(`${localizedKeyPrefix}description`, lngOption)
120
+ })];
121
+ }));
122
+ }
123
+ /**
124
+ * Generate by processing individual imports.
125
+ */
126
+ async generate() {
127
+ let success = false;
128
+ await i18nAppExtensionInit(I18nEnvironment.CLI);
129
+ this.initialize();
130
+ try {
131
+ for (const classDescriptor of getClassDescriptors().values()) {
132
+ const namespace = classDescriptor.namespace;
133
+ const namespacePrefix = namespace === undefined ? "" : `${namespace}.`;
134
+ const className = classDescriptor.name;
135
+ const methodInfix = classDescriptor.methodInfix;
136
+ // Namespace-qualified class name is used to construct object name.
137
+ const namespaceClassName = `${namespacePrefix}${className}`;
138
+ // First capture group is:
139
+ // - one or more uppercase letters followed by zero or more numbers; or
140
+ // - single uppercase letter followed by zero or more characters except uppercase letters or period.
141
+ // Second capture group is:
142
+ // - single uppercase letter followed by zero or more characters except period; or
143
+ // - zero characters (empty string).
144
+ // Third capture group, separated by optional period, is:
145
+ // - single uppercase letter followed by zero or more characters (remainder of string); or
146
+ // - zero characters (empty string).
147
+ const classNameMatch = /^([A-Z]+[0-9]*|[A-Z][^A-Z.]*)([A-Z][^.]*|)\.?([A-Z].*|)$/.exec(namespaceClassName);
148
+ if (classNameMatch === null) {
149
+ throw new Error(`${namespaceClassName} is not a valid namespace-qualified class name`);
150
+ }
151
+ const proxyObjectDescriptor = {
152
+ namespace,
153
+ className,
154
+ namespaceClassName,
155
+ classDescriptor,
156
+ objectName: `${classNameMatch[1].toLowerCase()}${classNameMatch[2]}${classNameMatch[3]}`
157
+ };
158
+ this.createProxyObject(proxyObjectDescriptor);
159
+ for (const methodDescriptor of classDescriptor.methodDescriptors) {
160
+ const methodName = methodDescriptor.name;
161
+ const infixBefore = methodDescriptor.infixBefore;
162
+ let functionName;
163
+ if (methodInfix === undefined || methodDescriptor.ignoreInfix === true) {
164
+ // No other classes in the hierarchy or no infix required.
165
+ functionName = methodName;
166
+ }
167
+ else if (infixBefore === undefined) {
168
+ // Other classes in the hierarchy and infix is postfix.
169
+ functionName = `${methodName}${methodInfix}`;
170
+ }
171
+ else {
172
+ const insertIndex = methodName.indexOf(infixBefore);
173
+ if (insertIndex === -1) {
174
+ throw new Error(`Cannot find "${infixBefore}" in method name ${methodName}`);
175
+ }
176
+ // Other classes in the hierarchy and infix is in the middle of the string.
177
+ functionName = `${methodName.substring(0, insertIndex)}${methodInfix}${methodName.substring(insertIndex)}`;
178
+ }
179
+ const namespaceFunctionName = `${namespacePrefix}${functionName}`;
180
+ const functionLocalizationsMap = this.generateLocalizationsMap(`Functions.${namespaceFunctionName}.`, (locale, localization) => ({
181
+ ...localization,
182
+ documentationURL: `${Generator.DOCUMENTATION_BASE_URL}${locale === this.defaultLocale ? "" : `${locale}/`}${Generator.DOCUMENTATION_PATH}${namespace === undefined ? "" : `${namespace}/`}${localization.name}.html`
183
+ }));
184
+ this._functionLocalizationsMapsMap.set(namespaceFunctionName, functionLocalizationsMap);
185
+ this.createProxyFunction({
186
+ ...proxyObjectDescriptor,
187
+ functionName,
188
+ namespaceFunctionName,
189
+ localizationsMap: functionLocalizationsMap,
190
+ proxyParameterDescriptors: methodDescriptor.parameterDescriptors.map((parameterDescriptor) => {
191
+ const expandedParameterDescriptor = expandParameterDescriptor(parameterDescriptor);
192
+ const parameterName = expandedParameterDescriptor.name;
193
+ const parameterLocalizationsMap = this.generateLocalizationsMap(`Parameters.${parameterName}.`, (_locale, localization) => localization);
194
+ this._parameterLocalizationsMapsMap.set(`${namespaceFunctionName}.${parameterName}`, parameterLocalizationsMap);
195
+ return {
196
+ namespace,
197
+ parameterName,
198
+ localizationsMap: parameterLocalizationsMap,
199
+ parameterDescriptor: expandedParameterDescriptor
200
+ };
201
+ }),
202
+ methodDescriptor
203
+ });
204
+ }
205
+ }
206
+ success = true;
207
+ }
208
+ finally {
209
+ await this.finalize(success);
210
+ }
211
+ }
212
+ }
213
+ //# sourceMappingURL=generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../src/generator/generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export type * from "./descriptor.js";
2
+ export * from "./generator.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generator/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,iBAAiB,CAAC;AACrC,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from "./generator.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/generator/index.ts"],"names":[],"mappings":"AACA,cAAc,gBAAgB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -22,4 +22,5 @@ export * from "./descriptor.js";
22
22
  export * from "./app-utility-proxy.js";
23
23
  export * from "./utility/index.js";
24
24
  export * as GS1 from "./gs1/index.js";
25
+ export * from "./generator/index.js";
25
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC;AACtC,cAAc,sBAAsB,CAAC"}
package/dist/index.js CHANGED
@@ -22,4 +22,5 @@ export * from "./descriptor.js";
22
22
  export * from "./app-utility-proxy.js";
23
23
  export * from "./utility/index.js";
24
24
  export * as GS1 from "./gs1/index.js";
25
+ export * from "./generator/index.js";
25
26
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC;AACtC,cAAc,sBAAsB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aidc-toolkit/app-extension",
3
- "version": "0.9.19-beta",
3
+ "version": "0.9.20-beta",
4
4
  "description": "Application extension framework for AIDC Toolkit",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,122 @@
1
+ import type { BaseParameterDescriptor, ClassDescriptor, MethodDescriptor } from "../descriptor.js";
2
+
3
+ /**
4
+ * Localization.
5
+ */
6
+ export interface Localization {
7
+ /**
8
+ * Name.
9
+ */
10
+ name: string;
11
+
12
+ /**
13
+ * Description.
14
+ */
15
+ description: string;
16
+ }
17
+
18
+ /**
19
+ * Function localization.
20
+ */
21
+ export interface FunctionLocalization extends Localization {
22
+ /**
23
+ * Documentation URL.
24
+ */
25
+ documentationURL: string;
26
+ }
27
+
28
+ /**
29
+ * Parameter localization.
30
+ */
31
+ export interface ParameterLocalization extends Localization {
32
+ }
33
+
34
+ /**
35
+ * Localization descriptor.
36
+ */
37
+ export interface LocalizationDescriptor<T extends Localization> {
38
+ /**
39
+ * Localizations map by locale.
40
+ */
41
+ readonly localizationsMap: ReadonlyMap<string, T>;
42
+ }
43
+
44
+ /**
45
+ * Proxy namespace descriptor.
46
+ */
47
+ export interface ProxyNamespaceDescriptor {
48
+ /**
49
+ * Namespace if any.
50
+ */
51
+ readonly namespace: string | undefined;
52
+ }
53
+
54
+ /**
55
+ * Proxy class descriptor.
56
+ */
57
+ export interface ProxyClassDescriptor extends ProxyNamespaceDescriptor {
58
+ /**
59
+ * Class name.
60
+ */
61
+ readonly className: string;
62
+
63
+ /**
64
+ * Namespace-qualified class name.
65
+ */
66
+ readonly namespaceClassName: string;
67
+
68
+ /**
69
+ * Class descriptor.
70
+ */
71
+ readonly classDescriptor: ClassDescriptor;
72
+ }
73
+
74
+ /**
75
+ * Proxy object descriptor.
76
+ */
77
+ export interface ProxyObjectDescriptor extends ProxyClassDescriptor {
78
+ /**
79
+ * Object name.
80
+ */
81
+ readonly objectName: string;
82
+ }
83
+
84
+ /**
85
+ * Proxy parameter descriptor.
86
+ */
87
+ export interface ProxyParameterDescriptor extends ProxyNamespaceDescriptor, LocalizationDescriptor<ParameterLocalization> {
88
+ /**
89
+ * Function name.
90
+ */
91
+ readonly parameterName: string;
92
+
93
+ /**
94
+ * Parameter descriptor.
95
+ */
96
+ readonly parameterDescriptor: BaseParameterDescriptor;
97
+ }
98
+
99
+ /**
100
+ * Proxy function descriptor.
101
+ */
102
+ export interface ProxyFunctionDescriptor extends ProxyObjectDescriptor, LocalizationDescriptor<FunctionLocalization> {
103
+ /**
104
+ * Function name.
105
+ */
106
+ readonly functionName: string;
107
+
108
+ /**
109
+ * Namespace-qualified function name.
110
+ */
111
+ readonly namespaceFunctionName: string;
112
+
113
+ /**
114
+ * Proxy parameter descriptors
115
+ */
116
+ readonly proxyParameterDescriptors: ProxyParameterDescriptor[];
117
+
118
+ /**
119
+ * Method descriptor.
120
+ */
121
+ readonly methodDescriptor: MethodDescriptor;
122
+ }
@@ -0,0 +1,287 @@
1
+ import { I18nEnvironment } from "@aidc-toolkit/core";
2
+ import type { ParseKeys } from "i18next";
3
+ import { expandParameterDescriptor, getClassDescriptors } from "../descriptor.js";
4
+ import { appExtensionResources, i18nAppExtensionInit, i18nextAppExtension } from "../locale/i18n.js";
5
+ import type {
6
+ FunctionLocalization,
7
+ Localization,
8
+ ParameterLocalization,
9
+ ProxyFunctionDescriptor,
10
+ ProxyObjectDescriptor
11
+ } from "./descriptor.js";
12
+
13
+ /**
14
+ * Abstract generator.
15
+ */
16
+ export abstract class Generator {
17
+ /**
18
+ * Documentation base URL.
19
+ */
20
+ private static readonly DOCUMENTATION_BASE_URL = "https://aidc-toolkit.com/";
21
+
22
+ /**
23
+ * Documentation path, optionally preceded by locale.
24
+ */
25
+ private static readonly DOCUMENTATION_PATH = "app-extension/";
26
+
27
+ /**
28
+ * Locales.
29
+ */
30
+ private readonly _locales: readonly string[];
31
+
32
+ /**
33
+ * Default locale.
34
+ */
35
+ private readonly _defaultLocale: string;
36
+
37
+ /**
38
+ * Map of function localizations maps by namespace function name.
39
+ */
40
+ private readonly _functionLocalizationsMapsMap = new Map<string, ReadonlyMap<string, FunctionLocalization>>();
41
+
42
+ /**
43
+ * Map of parameter localizations maps by namespace function parameter name.
44
+ */
45
+ private readonly _parameterLocalizationsMapsMap = new Map<string, ReadonlyMap<string, ParameterLocalization>>();
46
+
47
+ /**
48
+ *
49
+ */
50
+
51
+ /**
52
+ * Constructor.
53
+ *
54
+ * @param includeLocalizations
55
+ * Include localizations if true.
56
+ */
57
+ constructor(includeLocalizations = true) {
58
+ this._locales = includeLocalizations ? Object.keys(appExtensionResources) : [];
59
+ this._defaultLocale = this._locales[0] ?? "";
60
+ }
61
+
62
+ /**
63
+ * Get the locales.
64
+ */
65
+ protected get locales(): readonly string[] {
66
+ return this._locales;
67
+ }
68
+
69
+ /**
70
+ * Get the default locale.
71
+ */
72
+ protected get defaultLocale(): string {
73
+ return this._defaultLocale;
74
+ }
75
+
76
+ /**
77
+ * Get function localization.
78
+ *
79
+ * @param namespaceFunctionName
80
+ * Namespace function name.
81
+ *
82
+ * @param locale
83
+ * Locale.
84
+ *
85
+ * @returns
86
+ * Function localization.
87
+ */
88
+ protected getFunctionLocalization(namespaceFunctionName: string, locale: string): FunctionLocalization {
89
+ const functionLocalization = this._functionLocalizationsMapsMap.get(namespaceFunctionName)?.get(locale);
90
+
91
+ if (functionLocalization === undefined) {
92
+ throw new Error(`Localization for function "${namespaceFunctionName}", locale "${locale}" not found`);
93
+ }
94
+
95
+ return functionLocalization;
96
+ }
97
+
98
+ /**
99
+ * Get parameter localization.
100
+ *
101
+ * @param namespaceFunctionName
102
+ * Namespace function name.
103
+ *
104
+ * @param parameterName
105
+ * Parameter name.
106
+ *
107
+ * @param locale
108
+ * Locale.
109
+ *
110
+ * @returns
111
+ * Function localization.
112
+ */
113
+ protected getParameterLocalization(namespaceFunctionName: string, parameterName: string, locale: string): ParameterLocalization {
114
+ const parameterLocalization = this._parameterLocalizationsMapsMap.get(`${namespaceFunctionName}.${parameterName}`)?.get(locale);
115
+
116
+ if (parameterLocalization === undefined) {
117
+ throw new Error(`Localization for function "${namespaceFunctionName}", parameter "${parameterName}", locale "${locale}" not found`);
118
+ }
119
+
120
+ return parameterLocalization;
121
+ }
122
+
123
+ /**
124
+ * Initialize the generation of the output.
125
+ */
126
+ protected abstract initialize(): void;
127
+
128
+ /**
129
+ * Create a proxy object.
130
+ *
131
+ * @param proxyObjectDescriptor
132
+ * Proxy object descriptor.
133
+ */
134
+ protected abstract createProxyObject(proxyObjectDescriptor: ProxyObjectDescriptor): void;
135
+
136
+ /**
137
+ * Create a proxy function.
138
+ *
139
+ * @param proxyFunctionDescriptor
140
+ * Proxy function descriptor.
141
+ */
142
+ protected abstract createProxyFunction(proxyFunctionDescriptor: ProxyFunctionDescriptor): void;
143
+
144
+ /**
145
+ * Finalize the generation of the output.
146
+ *
147
+ * @param success
148
+ * True if successful.
149
+ */
150
+ protected abstract finalize(success: boolean): void | Promise<void>;
151
+
152
+ /**
153
+ * Generate localizations map.
154
+ *
155
+ * @param localizedKeyPrefix
156
+ * Localized key prefix.
157
+ *
158
+ * @param localizationCallback
159
+ * Callback to finalize localization.
160
+ *
161
+ * @returns
162
+ * Localization map.
163
+ */
164
+ private generateLocalizationsMap<T extends Localization>(localizedKeyPrefix: string, localizationCallback: (locale: string, localization: Localization) => T): ReadonlyMap<string, T> {
165
+ return new Map(this._locales.map((locale) => {
166
+ const lngOption = {
167
+ lng: locale
168
+ };
169
+
170
+ return [locale, localizationCallback(locale, {
171
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Localized key exists.
172
+ name: i18nextAppExtension.t(`${localizedKeyPrefix}name` as ParseKeys, lngOption),
173
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Localized key exists.
174
+ description: i18nextAppExtension.t(`${localizedKeyPrefix}description` as ParseKeys, lngOption)
175
+ })];
176
+ }));
177
+ }
178
+
179
+ /**
180
+ * Generate by processing individual imports.
181
+ */
182
+ async generate(): Promise<void> {
183
+ let success = false;
184
+
185
+ await i18nAppExtensionInit(I18nEnvironment.CLI);
186
+
187
+ this.initialize();
188
+
189
+ try {
190
+ for (const classDescriptor of getClassDescriptors().values()) {
191
+ const namespace = classDescriptor.namespace;
192
+ const namespacePrefix = namespace === undefined ? "" : `${namespace}.`;
193
+ const className = classDescriptor.name;
194
+ const methodInfix = classDescriptor.methodInfix;
195
+
196
+ // Namespace-qualified class name is used to construct object name.
197
+ const namespaceClassName = `${namespacePrefix}${className}`;
198
+
199
+ // First capture group is:
200
+ // - one or more uppercase letters followed by zero or more numbers; or
201
+ // - single uppercase letter followed by zero or more characters except uppercase letters or period.
202
+ // Second capture group is:
203
+ // - single uppercase letter followed by zero or more characters except period; or
204
+ // - zero characters (empty string).
205
+ // Third capture group, separated by optional period, is:
206
+ // - single uppercase letter followed by zero or more characters (remainder of string); or
207
+ // - zero characters (empty string).
208
+ const classNameMatch = /^([A-Z]+[0-9]*|[A-Z][^A-Z.]*)([A-Z][^.]*|)\.?([A-Z].*|)$/.exec(namespaceClassName);
209
+
210
+ if (classNameMatch === null) {
211
+ throw new Error(`${namespaceClassName} is not a valid namespace-qualified class name`);
212
+ }
213
+
214
+ const proxyObjectDescriptor: ProxyObjectDescriptor = {
215
+ namespace,
216
+ className,
217
+ namespaceClassName,
218
+ classDescriptor,
219
+ objectName: `${classNameMatch[1].toLowerCase()}${classNameMatch[2]}${classNameMatch[3]}`
220
+ };
221
+
222
+ this.createProxyObject(proxyObjectDescriptor);
223
+
224
+ for (const methodDescriptor of classDescriptor.methodDescriptors) {
225
+ const methodName = methodDescriptor.name;
226
+ const infixBefore = methodDescriptor.infixBefore;
227
+
228
+ let functionName: string;
229
+
230
+ if (methodInfix === undefined || methodDescriptor.ignoreInfix === true) {
231
+ // No other classes in the hierarchy or no infix required.
232
+ functionName = methodName;
233
+ } else if (infixBefore === undefined) {
234
+ // Other classes in the hierarchy and infix is postfix.
235
+ functionName = `${methodName}${methodInfix}`;
236
+ } else {
237
+ const insertIndex = methodName.indexOf(infixBefore);
238
+
239
+ if (insertIndex === -1) {
240
+ throw new Error(`Cannot find "${infixBefore}" in method name ${methodName}`);
241
+ }
242
+
243
+ // Other classes in the hierarchy and infix is in the middle of the string.
244
+ functionName = `${methodName.substring(0, insertIndex)}${methodInfix}${methodName.substring(insertIndex)}`;
245
+ }
246
+
247
+ const namespaceFunctionName = `${namespacePrefix}${functionName}`;
248
+
249
+ const functionLocalizationsMap = this.generateLocalizationsMap<FunctionLocalization>(`Functions.${namespaceFunctionName}.`, (locale, localization) => ({
250
+ ...localization,
251
+ documentationURL: `${Generator.DOCUMENTATION_BASE_URL}${locale === this.defaultLocale ? "" : `${locale}/`}${Generator.DOCUMENTATION_PATH}${namespace === undefined ? "" : `${namespace}/`}${localization.name}.html`
252
+ }));
253
+
254
+ this._functionLocalizationsMapsMap.set(namespaceFunctionName, functionLocalizationsMap);
255
+
256
+ this.createProxyFunction({
257
+ ...proxyObjectDescriptor,
258
+ functionName,
259
+ namespaceFunctionName,
260
+ localizationsMap: functionLocalizationsMap,
261
+ proxyParameterDescriptors: methodDescriptor.parameterDescriptors.map((parameterDescriptor) => {
262
+ const expandedParameterDescriptor = expandParameterDescriptor(parameterDescriptor);
263
+
264
+ const parameterName = expandedParameterDescriptor.name;
265
+
266
+ const parameterLocalizationsMap = this.generateLocalizationsMap(`Parameters.${parameterName}.`, (_locale, localization) => localization);
267
+
268
+ this._parameterLocalizationsMapsMap.set(`${namespaceFunctionName}.${parameterName}`, parameterLocalizationsMap);
269
+
270
+ return {
271
+ namespace,
272
+ parameterName,
273
+ localizationsMap: parameterLocalizationsMap,
274
+ parameterDescriptor: expandedParameterDescriptor
275
+ };
276
+ }),
277
+ methodDescriptor
278
+ });
279
+ }
280
+ }
281
+
282
+ success = true;
283
+ } finally {
284
+ await this.finalize(success);
285
+ }
286
+ }
287
+ }
@@ -0,0 +1,2 @@
1
+ export type * from "./descriptor.js";
2
+ export * from "./generator.js";
@@ -0,0 +1,358 @@
1
+ import type { LocaleStrings } from "@aidc-toolkit/core";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ import { expandParameterDescriptor, type ParameterDescriptor } from "../descriptor.js";
5
+ import type { ProxyFunctionDescriptor } from "./descriptor.js";
6
+ import { Generator } from "./generator.js";
7
+ import { logger } from "./logger.js";
8
+
9
+ /**
10
+ * Parameters sequencer entry.
11
+ */
12
+ interface ParametersSequencerEntry {
13
+ /**
14
+ * Parameters sequence or null if automatic.
15
+ */
16
+ parametersSequencerOrNull: ParametersSequencer | null;
17
+
18
+ /**
19
+ * Parameter descriptor.
20
+ */
21
+ parameterDescriptor: ParameterDescriptor;
22
+
23
+ /**
24
+ * True if parameter is actually used and not just a base.
25
+ */
26
+ isUsed: boolean;
27
+ }
28
+
29
+ /**
30
+ * Parameters sequencer for keeping similar (extended) parameters together.
31
+ */
32
+ type ParametersSequencer = Record<string, ParametersSequencerEntry>;
33
+
34
+ /**
35
+ * Format of locale strings module.
36
+ */
37
+ interface LocaleStringsModule {
38
+ /**
39
+ * Locale strings.
40
+ */
41
+ localeStrings: LocaleStrings;
42
+ }
43
+
44
+ /**
45
+ * Locale strings generator.
46
+ */
47
+ class LocaleStringsGenerator extends Generator {
48
+ /**
49
+ * Locale strings import path.
50
+ */
51
+ private static readonly IMPORT_PATH = "../app-extension/src/locale";
52
+
53
+ /**
54
+ * Parameters sequencer.
55
+ */
56
+ private readonly _parametersSequencer: ParametersSequencer = {};
57
+
58
+ /**
59
+ * Parameters locale strings.
60
+ */
61
+ private readonly _parametersLocaleStrings: LocaleStrings = {};
62
+
63
+ /**
64
+ * Functions locale strings.
65
+ */
66
+ private readonly _functionsLocaleStrings: LocaleStrings = {};
67
+
68
+ /**
69
+ * Locale strings.
70
+ */
71
+ private readonly _localeStrings: LocaleStrings = {
72
+ Parameters: this._parametersLocaleStrings,
73
+ Functions: this._functionsLocaleStrings
74
+ };
75
+
76
+ /**
77
+ * Constructor.
78
+ */
79
+ constructor() {
80
+ super(false);
81
+ }
82
+
83
+ /**
84
+ * @inheritDoc
85
+ */
86
+ protected initialize(): void {
87
+ }
88
+
89
+ /**
90
+ * @inheritDoc
91
+ */
92
+ protected createProxyObject(): void {
93
+ }
94
+
95
+ /**
96
+ * Save a parameter descriptor in the appropriate spot in the sequence.
97
+ *
98
+ * @param parameterDescriptor
99
+ * Parameter descriptor.
100
+ *
101
+ * @param isUsed
102
+ * True if parameter descriptor is actually used and not just a base.
103
+ *
104
+ * @returns
105
+ * Parameters sequencer entry.
106
+ */
107
+ private saveParameterSequence(parameterDescriptor: ParameterDescriptor, isUsed: boolean): ParametersSequencerEntry {
108
+ let parametersSequencerEntry: ParametersSequencerEntry;
109
+
110
+ if (!("extendsDescriptor" in parameterDescriptor)) {
111
+ const parameterName = parameterDescriptor.name;
112
+
113
+ // Create entry if it doesn't exist, otherwise pick up where last call left off.
114
+ if (!(parameterName in this._parametersSequencer)) {
115
+ parametersSequencerEntry = {
116
+ parametersSequencerOrNull: null,
117
+ parameterDescriptor,
118
+ isUsed
119
+ };
120
+
121
+ this._parametersSequencer[parameterName] = parametersSequencerEntry;
122
+ } else {
123
+ parametersSequencerEntry = this._parametersSequencer[parameterName];
124
+ }
125
+ } else {
126
+ const baseParametersSequencerEntry = this.saveParameterSequence(parameterDescriptor.extendsDescriptor, false);
127
+
128
+ const expandedParameterDescriptor = expandParameterDescriptor(parameterDescriptor);
129
+ const parameterName = expandedParameterDescriptor.name;
130
+
131
+ if (parameterName !== expandParameterDescriptor(parameterDescriptor.extendsDescriptor).name) {
132
+ // Make sure that base parameters sequencer entry refers to a record type.
133
+ baseParametersSequencerEntry.parametersSequencerOrNull ??= {};
134
+
135
+ if (!(parameterName in baseParametersSequencerEntry.parametersSequencerOrNull)) {
136
+ parametersSequencerEntry = {
137
+ parametersSequencerOrNull: null,
138
+ parameterDescriptor,
139
+ isUsed
140
+ };
141
+
142
+ baseParametersSequencerEntry.parametersSequencerOrNull[parameterName] = parametersSequencerEntry;
143
+ } else {
144
+ parametersSequencerEntry = baseParametersSequencerEntry.parametersSequencerOrNull[parameterName];
145
+ }
146
+ } else {
147
+ // If name is the same, which means that parameter descriptor modified type information only.
148
+ parametersSequencerEntry = baseParametersSequencerEntry;
149
+ }
150
+ }
151
+
152
+ return parametersSequencerEntry;
153
+ }
154
+
155
+ /**
156
+ * @inheritDoc
157
+ */
158
+ protected createProxyFunction(proxyFunctionDescriptor: ProxyFunctionDescriptor): void {
159
+ const {
160
+ namespace,
161
+ functionName,
162
+ methodDescriptor
163
+ } = proxyFunctionDescriptor;
164
+
165
+ // Add any parameters that are not already known.
166
+ for (const parameterDescriptor of methodDescriptor.parameterDescriptors) {
167
+ this.saveParameterSequence(parameterDescriptor, true);
168
+ }
169
+
170
+ let functionsLocaleStrings = this._functionsLocaleStrings;
171
+
172
+ if (namespace !== undefined) {
173
+ if (!(namespace in functionsLocaleStrings)) {
174
+ const namespaceFunctionsLocaleStrings: LocaleStrings = {};
175
+
176
+ // Add namespace and navigate to it.
177
+ functionsLocaleStrings[namespace] = namespaceFunctionsLocaleStrings;
178
+ functionsLocaleStrings = namespaceFunctionsLocaleStrings;
179
+ } else {
180
+ // Navigate to namespace.
181
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Entry is never a string.
182
+ functionsLocaleStrings = functionsLocaleStrings[namespace] as LocaleStrings;
183
+ }
184
+ }
185
+
186
+ if (functionName in functionsLocaleStrings) {
187
+ throw new Error(`Duplicate function "${functionName}"`);
188
+ }
189
+
190
+ // Add function.
191
+ functionsLocaleStrings[functionName] = {
192
+ name: functionName,
193
+ description: "*** LOCALIZATION REQUIRED ***"
194
+ };
195
+ }
196
+
197
+ /**
198
+ * Merge source locale strings into existing destination locale strings.
199
+ *
200
+ * @param logChanges
201
+ * If true, changes are logged. Limits output when processing multiple sources.
202
+ *
203
+ * @param parentKey
204
+ * Parent key for logging purposes.
205
+ *
206
+ * @param sourceLocaleStrings
207
+ * Source locale strings.
208
+ *
209
+ * @param destinationLocaleStrings
210
+ * Destination locale strings.
211
+ *
212
+ * @param addMissing
213
+ * Add missing if true; applies to locale strings that are not regional.
214
+ *
215
+ * @returns
216
+ * Merged locale strings.
217
+ */
218
+ private static merge(logChanges: boolean, parentKey: string, sourceLocaleStrings: LocaleStrings, destinationLocaleStrings: LocaleStrings, addMissing: boolean): LocaleStrings {
219
+ // Some entries at the top are not part of the generator output.
220
+ const deleteMissing = parentKey.length !== 0;
221
+
222
+ const newDestinationLocaleStrings: LocaleStrings = {};
223
+
224
+ // Copy over or delete any destination keys that are not in source.
225
+ for (const [key, destinationValue] of Object.entries(destinationLocaleStrings)) {
226
+ if (!(key in sourceLocaleStrings)) {
227
+ if (!deleteMissing) {
228
+ newDestinationLocaleStrings[key] = destinationValue;
229
+ } else if (logChanges) {
230
+ logger.info(`Deleting ${parentKey}${key}...`);
231
+ }
232
+ }
233
+ }
234
+
235
+ for (const [key, sourceValue] of Object.entries(sourceLocaleStrings)) {
236
+ if (!(key in destinationLocaleStrings)) {
237
+ if (addMissing) {
238
+ if (logChanges) {
239
+ logger.info(`Adding ${parentKey}${key}...`);
240
+ }
241
+
242
+ newDestinationLocaleStrings[key] = sourceValue;
243
+ }
244
+ } else {
245
+ const destinationValue = destinationLocaleStrings[key];
246
+
247
+ if (typeof sourceValue === "object" && typeof destinationValue === "object") {
248
+ newDestinationLocaleStrings[key] = LocaleStringsGenerator.merge(logChanges, `${parentKey}${key}.`, sourceValue, destinationValue, addMissing);
249
+ } else if (typeof sourceValue === "string" && typeof destinationValue === "string") {
250
+ newDestinationLocaleStrings[key] = destinationValue;
251
+ } else {
252
+ throw new Error(`Mismatched types at ${parentKey}${key}`);
253
+ }
254
+ }
255
+ }
256
+
257
+ return newDestinationLocaleStrings;
258
+ }
259
+
260
+ /**
261
+ * Build parameters locale strings by going through parameters sequencer.
262
+ *
263
+ * @param parametersSequencer
264
+ * Parameters sequencer.
265
+ */
266
+ private buildParametersLocaleStrings(parametersSequencer: ParametersSequencer): void {
267
+ const entries = Object.entries(parametersSequencer);
268
+
269
+ // Sort the entries as defined by the descriptors.
270
+ entries.sort((entry1, entry2) => {
271
+ let result: number;
272
+
273
+ const parameterDescriptor1 = entry1[1].parameterDescriptor;
274
+ const parameterDescriptor2 = entry2[1].parameterDescriptor;
275
+
276
+ if ("sortOrder" in parameterDescriptor1) {
277
+ if ("sortOrder" in parameterDescriptor2) {
278
+ result = parameterDescriptor1.sortOrder - parameterDescriptor2.sortOrder;
279
+ } else {
280
+ // Parameter descriptors with undefined sort order to go the end.
281
+ result = -parameterDescriptor1.sortOrder;
282
+ }
283
+ } else {
284
+ // eslint-disable-next-line no-lonely-if -- Matches structure in "then" clause.
285
+ if ("sortOrder" in parameterDescriptor2) {
286
+ // Parameter descriptors with undefined sort order to go the end.
287
+ result = parameterDescriptor2.sortOrder;
288
+ } else {
289
+ result = 0;
290
+ }
291
+ }
292
+
293
+ return result;
294
+ });
295
+
296
+ for (const [parameterName, parametersSequencerEntry] of entries) {
297
+ if (parametersSequencerEntry.isUsed) {
298
+ this._parametersLocaleStrings[parameterName] = {
299
+ name: parameterName,
300
+ description: ""
301
+ };
302
+ }
303
+
304
+ if (parametersSequencerEntry.parametersSequencerOrNull !== null) {
305
+ this.buildParametersLocaleStrings(parametersSequencerEntry.parametersSequencerOrNull);
306
+ }
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Build output to be written back to source file.
312
+ *
313
+ * @param prefix
314
+ * Line prefix.
315
+ *
316
+ * @param value
317
+ * Line value.
318
+ *
319
+ * @param indentLevel
320
+ * Indent level.
321
+ *
322
+ * @returns
323
+ * Output string.
324
+ */
325
+ private static buildOutput(prefix: string, value: LocaleStrings | string, indentLevel: number): string {
326
+ return `${" ".repeat(indentLevel)}${prefix} ${typeof value === "object" ?
327
+ `{\n${
328
+ Object.entries(value).map(entry => LocaleStringsGenerator.buildOutput(`${entry[0]}:`, entry[1], indentLevel + 1)).join(",\n")
329
+ }\n${" ".repeat(indentLevel)}}` :
330
+ // JSON.stringify() will apply quotes as appropriate.
331
+ JSON.stringify(value)
332
+ }`;
333
+ }
334
+
335
+ /**
336
+ * @inheritDoc
337
+ */
338
+ protected async finalize(success: boolean): Promise<void> {
339
+ if (success) {
340
+ this.buildParametersLocaleStrings(this._parametersSequencer);
341
+
342
+ await Promise.all(fs.readdirSync(LocaleStringsGenerator.IMPORT_PATH, {
343
+ withFileTypes: true
344
+ }).filter(entry => entry.isDirectory()).map(async (entry) => {
345
+ const localeStringsSource = path.resolve(LocaleStringsGenerator.IMPORT_PATH, entry.name, "locale-strings.ts");
346
+
347
+ await import(localeStringsSource).then((module) => {
348
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Module format is known.
349
+ const localeStrings = LocaleStringsGenerator.merge(entry.name === "en", "", this._localeStrings, (module as LocaleStringsModule).localeStrings, !entry.name.includes("-"));
350
+
351
+ fs.writeFileSync(localeStringsSource, `${LocaleStringsGenerator.buildOutput("export const localeStrings =", localeStrings, 0)};\n`);
352
+ });
353
+ }));
354
+ }
355
+ }
356
+ }
357
+
358
+ await new LocaleStringsGenerator().generate();
@@ -0,0 +1,34 @@
1
+ import { Logger } from "tslog";
2
+
3
+ /**
4
+ * Log level.
5
+ */
6
+ enum LogLevel {
7
+ Silly, Trace, Debug, Info, Warn, Error, Fatal
8
+ }
9
+
10
+ /**
11
+ * Logger with a default minimum level of Info.
12
+ */
13
+ export const logger = new Logger({
14
+ minLevel: LogLevel.Info
15
+ });
16
+
17
+ /**
18
+ * Set the log level.
19
+ *
20
+ * @param logLevel
21
+ * Log level as enumeration value or string.
22
+ */
23
+ export function setLogLevel(logLevel: LogLevel | string): void {
24
+ if (typeof logLevel === "string") {
25
+ if (logLevel in LogLevel) {
26
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- String exists as a key.
27
+ logger.settings.minLevel = LogLevel[logLevel as keyof typeof LogLevel];
28
+ } else {
29
+ logger.error(`Unknown log level ${logLevel}`);
30
+ }
31
+ } else {
32
+ logger.settings.minLevel = logLevel;
33
+ }
34
+ }
package/src/index.ts CHANGED
@@ -22,3 +22,4 @@ export * from "./descriptor.js";
22
22
  export * from "./app-utility-proxy.js";
23
23
  export * from "./utility/index.js";
24
24
  export * as GS1 from "./gs1/index.js";
25
+ export * from "./generator/index.js";