@keycloakify/svelte 0.1.3 → 0.1.5

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.
Files changed (50) hide show
  1. package/keycloakify-svelte/bin/200.index.js +1 -1
  2. package/keycloakify-svelte/bin/266.index.js +1 -1
  3. package/keycloakify-svelte/bin/279.index.js +194 -48
  4. package/keycloakify-svelte/bin/709.index.js +1 -1
  5. package/keycloakify-svelte/bin/818.index.js +1 -1
  6. package/keycloakify-svelte/login/DefaultPage.svelte.d.ts +4 -4
  7. package/keycloakify-svelte/login/components/AddRemoveButtonsMultiValuedAttribute.svelte.d.ts +4 -3
  8. package/keycloakify-svelte/login/components/FieldErrors.svelte.d.ts +3 -3
  9. package/keycloakify-svelte/login/components/GroupLabel.svelte.d.ts +4 -4
  10. package/keycloakify-svelte/login/components/InputTag.svelte.d.ts +1 -1
  11. package/keycloakify-svelte/login/components/LogoutOtherSessions.svelte.d.ts +3 -3
  12. package/keycloakify-svelte/login/components/PasswordWrapper.svelte.d.ts +4 -4
  13. package/keycloakify-svelte/login/components/TermsAcceptance.svelte.d.ts +4 -3
  14. package/keycloakify-svelte/login/pages/IdpReviewUserProfile.svelte.d.ts +5 -2
  15. package/keycloakify-svelte/login/pages/LoginUpdateProfile.svelte.d.ts +5 -2
  16. package/keycloakify-svelte/login/pages/Register.svelte.d.ts +5 -2
  17. package/keycloakify-svelte/login/pages/UpdateEmail.svelte.d.ts +5 -2
  18. package/package.json +26 -24
  19. package/src/bin/add-story.ts +117 -0
  20. package/src/bin/core.ts +10 -0
  21. package/src/bin/eject-page.ts +233 -0
  22. package/src/bin/initialize-account-theme/boilerplate/KcContext.ts +11 -0
  23. package/src/bin/initialize-account-theme/boilerplate/KcPage.svelte +28 -0
  24. package/src/bin/initialize-account-theme/boilerplate/KcPageStory.svelte +9 -0
  25. package/src/bin/initialize-account-theme/boilerplate/KcPageStory.ts +22 -0
  26. package/src/bin/initialize-account-theme/boilerplate/i18n.ts +9 -0
  27. package/src/bin/initialize-account-theme/index.ts +1 -0
  28. package/src/bin/initialize-account-theme/initialize-account-theme.ts +87 -0
  29. package/src/bin/initialize-account-theme/updateAccountThemeImplementationInConfig.ts +93 -0
  30. package/src/bin/main.ts +43 -0
  31. package/src/bin/tools/SemVer.ts +107 -0
  32. package/src/bin/tools/String.prototype.replaceAll.ts +31 -0
  33. package/src/bin/tools/crawl.ts +32 -0
  34. package/src/bin/tools/fs.rmSync.ts +34 -0
  35. package/src/bin/tools/getThisCodebaseRootDirPath.ts +22 -0
  36. package/src/bin/tools/kebabCaseToSnakeCase.ts +7 -0
  37. package/src/bin/tools/nodeModulesBinDirPath.ts +38 -0
  38. package/src/bin/tools/readThisNpmPackageVersion.ts +22 -0
  39. package/src/bin/tools/runPrettier.ts +118 -0
  40. package/src/bin/tools/transformCodebase.ts +81 -0
  41. package/src/bin/tools/transformCodebase_async.ts +82 -0
  42. package/src/bin/tsconfig.json +23 -0
  43. package/src/bin/update-kc-gen.ts +153 -0
  44. package/src/tools/useConst.ts +4 -0
  45. package/src/tools/useInsertLinkTags.ts +81 -0
  46. package/src/tools/useInsertScriptTags.ts +110 -0
  47. package/src/tools/useReducer.ts +11 -0
  48. package/src/tools/useSetClassName.ts +18 -0
  49. package/src/tools/useState.ts +8 -0
  50. package/stories/login/KcPage.svelte +10 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keycloakify/svelte",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Svelte Components for Keycloakify",
5
5
  "keywords": [
6
6
  "keycloak",
@@ -415,8 +415,6 @@
415
415
  "src",
416
416
  "!src/**/*.test.*",
417
417
  "!src/**/*.spec.*",
418
- "!src/bin",
419
- "!src/tools",
420
418
  "package.json",
421
419
  "README.md",
422
420
  "LICENSE"
@@ -429,39 +427,43 @@
429
427
  "svelte:check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
430
428
  "_format": "prettier '**/*.{ts,html,json,md,svelte}'",
431
429
  "format": "yarn _format --write",
432
- "lint": "yarn format && eslint . --fix"
430
+ "lint": "yarn format && eslint . --fix",
431
+ "changelog": "npx conventional-changelog -t -p angular -i CHANGELOG.md -s -r 0",
432
+ "release": "sh scripts/release.sh"
433
433
  },
434
434
  "peerDependencies": {
435
435
  "keycloakify": "^11.7.0",
436
436
  "svelte": "^5.0.0"
437
437
  },
438
438
  "devDependencies": {
439
- "@sveltejs/adapter-auto": "^3.3.1",
440
- "@sveltejs/kit": "^2.8.3",
441
- "@sveltejs/package": "^2.3.7",
442
- "@sveltejs/vite-plugin-svelte": "^4.0.2",
439
+ "@sveltejs/adapter-auto": "^4.0.0",
440
+ "@sveltejs/kit": "^2.16.1",
441
+ "@sveltejs/package": "^2.3.9",
442
+ "@sveltejs/vite-plugin-svelte": "^5.0.3",
443
443
  "@types/eslint": "^9.6.1",
444
- "@types/node": "^22.9.3",
444
+ "@types/node": "^22.10.7",
445
445
  "@vercel/ncc": "^0.38.3",
446
+ "conventional-changelog-cli": "^5.0.0",
447
+ "cz-conventional-changelog": "^3.3.0",
446
448
  "cli-select": "^1.1.2",
447
- "eslint": "9.15.0",
448
- "eslint-config-prettier": "^9.1.0",
449
- "eslint-plugin-prettier": "^5.2.1",
450
- "eslint-plugin-svelte": "^2.46.0",
449
+ "eslint": "9.18.0",
450
+ "eslint-config-prettier": "^10.0.1",
451
+ "eslint-plugin-prettier": "^5.2.3",
452
+ "eslint-plugin-svelte": "^2.46.1",
451
453
  "eslint-plugin-unused-imports": "^4.1.4",
452
- "globals": "^15.12.0",
453
- "keycloakify": "^11.7.0",
454
- "npm-check-updates": "^17.1.11",
455
- "prettier": "^3.3.3",
456
- "prettier-plugin-svelte": "^3.3.2",
457
- "publint": "^0.2.12",
458
- "svelte": "^5.2.8",
459
- "svelte-check": "^4.1.0",
454
+ "globals": "^15.14.0",
455
+ "keycloakify": "^11.8.9",
456
+ "npm-check-updates": "^17.1.14",
457
+ "prettier": "^3.4.2",
458
+ "prettier-plugin-svelte": "^3.3.3",
459
+ "publint": "0.2.12",
460
+ "svelte": "^5.19.1",
461
+ "svelte-check": "^4.1.4",
460
462
  "tsx": "^4.19.2",
461
463
  "typescript": "~5.6.3",
462
- "typescript-eslint": "^8.15.0",
463
- "vite": "^5.4.11",
464
- "zod": "^3.23.8"
464
+ "typescript-eslint": "^8.21.0",
465
+ "vite": "^6.0.11",
466
+ "zod": "^3.24.1"
465
467
  },
466
468
  "bin": {
467
469
  "_keycloakify-custom-handler": "keycloakify-svelte/bin/index.js"
@@ -0,0 +1,117 @@
1
+ import chalk from 'chalk';
2
+ import cliSelect from 'cli-select';
3
+ import * as fs from 'fs';
4
+ import { dirname as pathDirname, join as pathJoin, relative as pathRelative } from 'path';
5
+ import { assert, Equals } from 'tsafe/assert';
6
+ import { capitalize } from 'tsafe/capitalize';
7
+ import {
8
+ ACCOUNT_THEME_PAGE_IDS,
9
+ type AccountThemePageId,
10
+ type BuildContext,
11
+ LOGIN_THEME_PAGE_IDS,
12
+ type LoginThemePageId,
13
+ THEME_TYPES,
14
+ } from './core';
15
+ import { getThisCodebaseRootDirPath } from './tools/getThisCodebaseRootDirPath';
16
+ import { kebabCaseToCamelCase } from './tools/kebabCaseToSnakeCase';
17
+ import { getIsPrettierAvailable, runPrettier } from './tools/runPrettier';
18
+
19
+ export async function command(params: { buildContext: BuildContext }) {
20
+ const { buildContext } = params;
21
+
22
+ console.log(chalk.cyan('Theme type:'));
23
+
24
+ const themeType = await (async () => {
25
+ const values = THEME_TYPES.filter((themeType) => {
26
+ switch (themeType) {
27
+ case 'account':
28
+ return buildContext.implementedThemeTypes.account.isImplemented;
29
+ case 'login':
30
+ return buildContext.implementedThemeTypes.login.isImplemented;
31
+ case 'admin':
32
+ return false;
33
+ }
34
+ assert<Equals<typeof themeType, never>>(false);
35
+ });
36
+
37
+ assert(values.length > 0, 'No theme is implemented in this project');
38
+
39
+ if (values.length === 1) {
40
+ return values[0];
41
+ }
42
+
43
+ const { value } = await cliSelect({
44
+ values,
45
+ }).catch(() => {
46
+ process.exit(-1);
47
+ });
48
+
49
+ return value;
50
+ })();
51
+
52
+ console.log(`→ ${themeType}`);
53
+
54
+ console.log(chalk.cyan('Select the page you want to create a Storybook for:'));
55
+
56
+ const { value: pageId } = await cliSelect<LoginThemePageId | AccountThemePageId>({
57
+ values: (() => {
58
+ switch (themeType) {
59
+ case 'login':
60
+ return [...LOGIN_THEME_PAGE_IDS];
61
+ case 'account':
62
+ return [...ACCOUNT_THEME_PAGE_IDS];
63
+ case 'admin':
64
+ return [];
65
+ }
66
+ assert<Equals<typeof themeType, never>>(false);
67
+ })(),
68
+ }).catch(() => {
69
+ process.exit(-1);
70
+ });
71
+
72
+ console.log(`→ ${pageId}`);
73
+
74
+ const componentBasename = capitalize(kebabCaseToCamelCase(pageId.replace(/ftl$/, ''))) + 'stories.svelte';
75
+
76
+ const targetFilePath = pathJoin(buildContext.themeSrcDirPath, themeType, 'pages', componentBasename);
77
+
78
+ if (fs.existsSync(targetFilePath)) {
79
+ console.log(`${pathRelative(process.cwd(), targetFilePath)} already exists`);
80
+
81
+ process.exit(-1);
82
+ }
83
+
84
+ let sourceCode = fs
85
+ .readFileSync(pathJoin(getThisCodebaseRootDirPath(), 'stories', themeType, 'pages', componentBasename))
86
+ .toString('utf8');
87
+
88
+ run_prettier: {
89
+ if (!(await getIsPrettierAvailable())) {
90
+ break run_prettier;
91
+ }
92
+
93
+ sourceCode = await runPrettier({
94
+ filePath: targetFilePath,
95
+ sourceCode: sourceCode,
96
+ });
97
+ }
98
+
99
+ {
100
+ const targetDirPath = pathDirname(targetFilePath);
101
+
102
+ if (!fs.existsSync(targetDirPath)) {
103
+ fs.mkdirSync(targetDirPath, { recursive: true });
104
+ }
105
+ }
106
+
107
+ fs.writeFileSync(targetFilePath, Buffer.from(sourceCode, 'utf8'));
108
+
109
+ console.log(
110
+ [
111
+ `${chalk.green('✓')} ${chalk.bold(
112
+ pathJoin('.', pathRelative(process.cwd(), targetFilePath)),
113
+ )} copy pasted from the Keycloakify source code into your project`,
114
+ `You can start storybook with ${chalk.bold('yarn run storybook')}`,
115
+ ].join('\n'),
116
+ );
117
+ }
@@ -0,0 +1,10 @@
1
+ export type { BuildContext } from 'keycloakify/bin/shared/buildContext';
2
+ export {
3
+ ACCOUNT_THEME_PAGE_IDS,
4
+ LOGIN_THEME_PAGE_IDS,
5
+ THEME_TYPES,
6
+ type AccountThemePageId,
7
+ type LoginThemePageId,
8
+ type ThemeType,
9
+ } from 'keycloakify/bin/shared/constants';
10
+ export { BIN_NAME, NOT_IMPLEMENTED_EXIT_CODE, readParams } from 'keycloakify/bin/shared/customHandler';
@@ -0,0 +1,233 @@
1
+ #!/usr/bin/env node
2
+
3
+ import chalk from 'chalk';
4
+ import cliSelect from 'cli-select';
5
+ import * as fs from 'fs';
6
+ import { dirname as pathDirname, join as pathJoin, relative as pathRelative } from 'path';
7
+ import { assert, Equals } from 'tsafe/assert';
8
+ import { capitalize } from 'tsafe/capitalize';
9
+ import {
10
+ ACCOUNT_THEME_PAGE_IDS,
11
+ type AccountThemePageId,
12
+ type BuildContext,
13
+ LOGIN_THEME_PAGE_IDS,
14
+ type LoginThemePageId,
15
+ THEME_TYPES,
16
+ } from './core';
17
+ import { getThisCodebaseRootDirPath } from './tools/getThisCodebaseRootDirPath';
18
+ import { kebabCaseToCamelCase } from './tools/kebabCaseToSnakeCase';
19
+ import { getIsPrettierAvailable, runPrettier } from './tools/runPrettier';
20
+
21
+ export async function command(params: { buildContext: BuildContext }) {
22
+ const { buildContext } = params;
23
+
24
+ console.log(chalk.cyan('Theme type:'));
25
+
26
+ const themeType = await (async () => {
27
+ const values = THEME_TYPES.filter((themeType) => {
28
+ switch (themeType) {
29
+ case 'account':
30
+ return buildContext.implementedThemeTypes.account.isImplemented;
31
+ case 'login':
32
+ return buildContext.implementedThemeTypes.login.isImplemented;
33
+ case 'admin':
34
+ return buildContext.implementedThemeTypes.admin.isImplemented;
35
+ }
36
+ assert<Equals<typeof themeType, never>>(false);
37
+ });
38
+
39
+ assert(values.length > 0, 'No theme is implemented in this project');
40
+
41
+ if (values.length === 1) {
42
+ return values[0];
43
+ }
44
+
45
+ const { value } = await cliSelect({
46
+ values,
47
+ }).catch(() => {
48
+ process.exit(-1);
49
+ });
50
+
51
+ return value;
52
+ })();
53
+
54
+ console.log(`→ ${themeType}`);
55
+
56
+ console.log(chalk.cyan('Select the page you want to customize:'));
57
+
58
+ const templateValue = 'Template.svelte (Layout common to every page)';
59
+ const userProfileFormFieldsValue =
60
+ 'UserProfileFormFields.svelte (Renders the form of the register.ftl, login-update-profile.ftl, update-email.ftl and idp-review-user-profile.ftl)';
61
+ const otherPageValue = "The page you're looking for isn't listed here";
62
+
63
+ const { value: pageIdOrComponent } = await cliSelect<
64
+ | LoginThemePageId
65
+ | AccountThemePageId
66
+ | typeof templateValue
67
+ | typeof userProfileFormFieldsValue
68
+ | typeof otherPageValue
69
+ >({
70
+ values: (() => {
71
+ switch (themeType) {
72
+ case 'login':
73
+ return [templateValue, userProfileFormFieldsValue, ...LOGIN_THEME_PAGE_IDS, otherPageValue];
74
+ case 'account':
75
+ return [templateValue, ...ACCOUNT_THEME_PAGE_IDS, otherPageValue];
76
+ case 'admin':
77
+ return [];
78
+ }
79
+ assert<Equals<typeof themeType, never>>(false);
80
+ })(),
81
+ }).catch(() => {
82
+ process.exit(-1);
83
+ });
84
+
85
+ if (pageIdOrComponent === otherPageValue) {
86
+ console.log(
87
+ [
88
+ 'To style a page not included in the base Keycloak, such as one added by a third-party Keycloak extension,',
89
+ 'refer to the documentation: https://docs.keycloakify.dev/features/styling-a-custom-page-not-included-in-base-keycloak',
90
+ ].join(' '),
91
+ );
92
+
93
+ process.exit(0);
94
+ }
95
+
96
+ console.log(`→ ${pageIdOrComponent}`);
97
+
98
+ const componentBasename = (() => {
99
+ if (pageIdOrComponent === templateValue) {
100
+ return 'Template.svelte';
101
+ }
102
+
103
+ if (pageIdOrComponent === userProfileFormFieldsValue) {
104
+ return 'UserProfileFormFields.svelte';
105
+ }
106
+
107
+ return capitalize(kebabCaseToCamelCase(pageIdOrComponent)).replace(/ftl$/, 'svelte');
108
+ })();
109
+
110
+ const pagesOrDot = (() => {
111
+ if (pageIdOrComponent === templateValue || pageIdOrComponent === userProfileFormFieldsValue) {
112
+ return '.';
113
+ }
114
+
115
+ return 'pages';
116
+ })();
117
+
118
+ const targetFilePath = pathJoin(buildContext.themeSrcDirPath, themeType, pagesOrDot, componentBasename);
119
+
120
+ if (fs.existsSync(targetFilePath)) {
121
+ console.log(
122
+ `${pageIdOrComponent} is already ejected, ${pathRelative(process.cwd(), targetFilePath)} already exists`,
123
+ );
124
+
125
+ process.exit(-1);
126
+ }
127
+
128
+ let componentCode = fs
129
+ .readFileSync(pathJoin(getThisCodebaseRootDirPath(), 'src', themeType, pagesOrDot, componentBasename))
130
+ .toString('utf8');
131
+
132
+ run_prettier: {
133
+ if (!(await getIsPrettierAvailable())) {
134
+ break run_prettier;
135
+ }
136
+
137
+ componentCode = await runPrettier({
138
+ filePath: targetFilePath,
139
+ sourceCode: componentCode,
140
+ });
141
+ }
142
+
143
+ {
144
+ const targetDirPath = pathDirname(targetFilePath);
145
+
146
+ if (!fs.existsSync(targetDirPath)) {
147
+ fs.mkdirSync(targetDirPath, { recursive: true });
148
+ }
149
+ }
150
+
151
+ fs.writeFileSync(targetFilePath, Buffer.from(componentCode, 'utf8'));
152
+
153
+ console.log(
154
+ `${chalk.green('✓')} ${chalk.bold(
155
+ pathJoin('.', pathRelative(process.cwd(), targetFilePath)),
156
+ )} copy pasted from the Keycloakify source code into your project`,
157
+ );
158
+
159
+ edit_KcPage: {
160
+ if (pageIdOrComponent !== templateValue && pageIdOrComponent !== userProfileFormFieldsValue) {
161
+ break edit_KcPage;
162
+ }
163
+
164
+ const kcAppSveltePath = pathJoin(buildContext.themeSrcDirPath, themeType, 'KcPage.svelte');
165
+
166
+ const kcAppSvelteCode = fs.readFileSync(kcAppSveltePath).toString('utf8');
167
+
168
+ const modifiedKcAppSvelteCode = (() => {
169
+ switch (pageIdOrComponent) {
170
+ case templateValue:
171
+ return kcAppSvelteCode.replace(`@keycloakify/svelte/${themeType}/Template`, './Template');
172
+ case userProfileFormFieldsValue:
173
+ return kcAppSvelteCode.replace(`@keycloakify/svelte/login/UserProfileFormFields`, './UserProfileFormFields');
174
+ }
175
+ assert<Equals<typeof pageIdOrComponent, never>>(false);
176
+ })();
177
+
178
+ if (kcAppSvelteCode === modifiedKcAppSvelteCode) {
179
+ console.log(chalk.red('Unable to automatically update KcPage.svelte, please update it manually'));
180
+ return;
181
+ }
182
+
183
+ fs.writeFileSync(kcAppSveltePath, Buffer.from(modifiedKcAppSvelteCode, 'utf8'));
184
+
185
+ console.log(
186
+ `${chalk.green('✓')} ${chalk.bold(pathJoin('.', pathRelative(process.cwd(), kcAppSveltePath)))} Updated`,
187
+ );
188
+
189
+ return;
190
+ }
191
+
192
+ console.log(
193
+ [
194
+ ``,
195
+ `You now need to update your page router:`,
196
+ ``,
197
+ `${chalk.bold(
198
+ pathJoin('.', pathRelative(process.cwd(), buildContext.themeSrcDirPath), themeType, 'KcPage.svelte'),
199
+ )}:`,
200
+ chalk.grey('```'),
201
+ `// ...`,
202
+ ``,
203
+ ...(() => {
204
+ let inGreenBlock = false;
205
+ return [
206
+ ` const page = async () => {`,
207
+ ` switch (kcContext.pageId) {`,
208
+ `+`,
209
+ ` case '${pageIdOrComponent}':`,
210
+ ` return import('./pages/${componentBasename}"');`,
211
+ `+`,
212
+ ` //...`,
213
+ ` default:`,
214
+ ` return import('@keycloakify/svelte/login/DefaultPage.svelte');`,
215
+ ` }`,
216
+ ` }`,
217
+ ].map((line) => {
218
+ if (line === `+`) {
219
+ inGreenBlock = !inGreenBlock;
220
+ }
221
+ if (inGreenBlock || line.startsWith('+')) {
222
+ return chalk.green(line);
223
+ }
224
+ if (line.startsWith('-')) {
225
+ return chalk.red(line);
226
+ }
227
+ return chalk.grey(line);
228
+ });
229
+ })(),
230
+ chalk.grey('```'),
231
+ ].join('\n'),
232
+ );
233
+ }
@@ -0,0 +1,11 @@
1
+ import type { ExtendKcContext } from 'keycloakify/account';
2
+ import type { KcEnvName, ThemeName } from '../kc.gen';
3
+
4
+ export type KcContextExtension = {
5
+ themeName: ThemeName;
6
+ properties: Record<KcEnvName, string> & {};
7
+ };
8
+
9
+ export type KcContextExtensionPerPage = Record<string, Record<string, unknown>>;
10
+
11
+ export type KcContext = ExtendKcContext<KcContextExtension, KcContextExtensionPerPage>;
@@ -0,0 +1,28 @@
1
+ <script lang="ts">
2
+ import Template from '@keycloakify/svelte/account/Template.svelte';
3
+ import type { KcContext } from 'keycloakify/account/KcContext';
4
+ import type { ClassKey } from 'keycloakify/account/lib/kcClsx';
5
+ import { useI18n } from './i18n';
6
+
7
+ const { kcContext }: { kcContext: KcContext } = $props();
8
+
9
+ const { i18n } = useI18n({ kcContext });
10
+ const page = async () => {
11
+ switch (kcContext.pageId) {
12
+ default:
13
+ return import('@keycloakify/svelte/account/DefaultPage.svelte');
14
+ }
15
+ };
16
+
17
+ const classes = {} satisfies { [key in ClassKey]?: string };
18
+ </script>
19
+
20
+ {#await page() then { default: Page }}
21
+ <Page
22
+ {kcContext}
23
+ {i18n}
24
+ {classes}
25
+ {Template}
26
+ doUseDefaultCss={true}
27
+ ></Page>
28
+ {/await}
@@ -0,0 +1,9 @@
1
+ <script lang="ts">
2
+ import KcPage from './KcPage.svelte';
3
+ import { getKcContextMock, type KcPageStoryProps } from './KcPageStory';
4
+
5
+ const { pageId, kcContext }: KcPageStoryProps = $props();
6
+ const kcContextMock = getKcContextMock({ pageId, overrides: kcContext });
7
+ </script>
8
+
9
+ <KcPage kcContext={kcContextMock} />
@@ -0,0 +1,22 @@
1
+ import { createGetKcContextMock } from 'keycloakify/account/KcContext';
2
+ import type { DeepPartial } from 'keycloakify/tools/DeepPartial';
3
+ import { kcEnvDefaults, themeNames } from '../kc.gen';
4
+ import type { KcContext, KcContextExtension, KcContextExtensionPerPage } from './KcContext';
5
+
6
+ const kcContextExtension: KcContextExtension = {
7
+ themeName: themeNames[0],
8
+ properties: {
9
+ ...kcEnvDefaults,
10
+ },
11
+ };
12
+ const kcContextExtensionPerPage: KcContextExtensionPerPage = {};
13
+
14
+ export const { getKcContextMock } = createGetKcContextMock({
15
+ kcContextExtension,
16
+ kcContextExtensionPerPage,
17
+ overrides: {},
18
+ overridesPerPage: {},
19
+ });
20
+
21
+ type PageId = KcContext['pageId'];
22
+ export type KcPageStoryProps = { pageId: PageId; kcContext?: DeepPartial<Extract<KcContext, { pageId: PageId }>> };
@@ -0,0 +1,9 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import { i18nBuilder } from '@keycloakify/svelte/account';
3
+ import type { ThemeName } from '../kc.gen';
4
+
5
+ /** @see: https://docs.keycloakify.dev/features/i18n */
6
+ const { useI18n, ofTypeI18n } = i18nBuilder.withThemeName<ThemeName>().build();
7
+
8
+ type I18n = typeof ofTypeI18n;
9
+ export { useI18n, type I18n };
@@ -0,0 +1 @@
1
+ export * from './initialize-account-theme';
@@ -0,0 +1,87 @@
1
+ import chalk from 'chalk';
2
+ import child_process from 'child_process';
3
+ import * as fs from 'fs';
4
+ import { join as pathJoin, relative as pathRelative } from 'path';
5
+ import type { BuildContext } from '../core';
6
+ import { getThisCodebaseRootDirPath } from '../tools/getThisCodebaseRootDirPath';
7
+ import { command as updateKcGenCommand } from '../update-kc-gen';
8
+ import { updateAccountThemeImplementationInConfig } from './updateAccountThemeImplementationInConfig';
9
+
10
+ export async function command(params: { buildContext: BuildContext }) {
11
+ const { buildContext } = params;
12
+
13
+ const accountThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, 'account');
14
+
15
+ if (fs.existsSync(accountThemeSrcDirPath) && fs.readdirSync(accountThemeSrcDirPath).length > 0) {
16
+ console.warn(
17
+ chalk.red(
18
+ `There is already a ${pathRelative(
19
+ process.cwd(),
20
+ accountThemeSrcDirPath,
21
+ )} directory in your project. Aborting.`,
22
+ ),
23
+ );
24
+
25
+ process.exit(-1);
26
+ }
27
+
28
+ exit_if_uncommitted_changes: {
29
+ let hasUncommittedChanges: boolean | undefined = undefined;
30
+
31
+ try {
32
+ hasUncommittedChanges =
33
+ child_process
34
+ .execSync(`git status --porcelain`, {
35
+ cwd: buildContext.projectDirPath,
36
+ })
37
+ .toString()
38
+ .trim() !== '';
39
+ } catch {
40
+ // Probably not a git repository
41
+ break exit_if_uncommitted_changes;
42
+ }
43
+
44
+ if (!hasUncommittedChanges) {
45
+ break exit_if_uncommitted_changes;
46
+ }
47
+ console.warn(
48
+ [
49
+ chalk.red('Please commit or stash your changes before running this command.\n'),
50
+ "This command will modify your project's files so it's better to have a clean working directory",
51
+ 'so that you can easily see what has been changed and revert if needed.',
52
+ ].join(' '),
53
+ );
54
+
55
+ process.exit(-1);
56
+ }
57
+
58
+ const accountThemeType = 'Multi-Page';
59
+
60
+ fs.cpSync(
61
+ pathJoin(getThisCodebaseRootDirPath(), 'src', 'bin', 'initialize-account-theme', 'boilerplate'),
62
+ accountThemeSrcDirPath,
63
+ { recursive: true },
64
+ );
65
+
66
+ updateAccountThemeImplementationInConfig({ buildContext, accountThemeType });
67
+
68
+ updateKcGenCommand({
69
+ buildContext: {
70
+ ...buildContext,
71
+ implementedThemeTypes: {
72
+ ...buildContext.implementedThemeTypes,
73
+ account: {
74
+ isImplemented: true,
75
+ type: accountThemeType,
76
+ },
77
+ },
78
+ },
79
+ });
80
+
81
+ console.log(
82
+ [
83
+ chalk.green(`The ${accountThemeType} account theme has been initialized.`),
84
+ `Directory created: ${chalk.bold(pathRelative(process.cwd(), accountThemeSrcDirPath))}`,
85
+ ].join('\n'),
86
+ );
87
+ }