Compare commits
142 Commits
Author | SHA1 | Date | |
---|---|---|---|
59db202fe4 | |||
09927afd43 | |||
13c6122b9b | |||
1bb19f65a2 | |||
918a80cfb6 | |||
ed7d5eabcb | |||
2795109162 | |||
966f277628 | |||
36d60411f9 | |||
60fe33f3fd | |||
1df685df92 | |||
7894d95ace | |||
a8b4493aa1 | |||
715a7399cf | |||
a1e59bae23 | |||
b0819314a1 | |||
0099442543 | |||
66a0b07228 | |||
85f9544754 | |||
2f16a09ab8 | |||
183ae98c30 | |||
ba15e63879 | |||
654277feda | |||
81279a5cc5 | |||
59f0a843b0 | |||
c094f70171 | |||
0858fe6319 | |||
5012ec0ccc | |||
990a24fab2 | |||
036b6bf82a | |||
8272a02b52 | |||
e346b1d9d2 | |||
2309bd21c6 | |||
7d6476c1b5 | |||
e892a0e7e6 | |||
ca5b41e730 | |||
9b18234112 | |||
5274368f47 | |||
1415c24028 | |||
4a084f5859 | |||
a30c9eb0cd | |||
85d3b40b8e | |||
335afec230 | |||
69fa49848a | |||
7a09051127 | |||
07ee0ecb8b | |||
6f133428f8 | |||
4f733736db | |||
d96ff13a67 | |||
2c1351ce47 | |||
96cd56ec77 | |||
e5b2096d65 | |||
3aa140335f | |||
4cafaa2492 | |||
9c633a7521 | |||
e27845ba91 | |||
2a8708a45b | |||
6874fa4c24 | |||
ba531a4927 | |||
20175b57cf | |||
ad275e4c34 | |||
060b9fe0de | |||
17b24d14ed | |||
2d278b0680 | |||
fb5975e4f1 | |||
24fccaf513 | |||
293953aa1b | |||
1049e312f9 | |||
a2db250600 | |||
cf7fe8c337 | |||
f5350097bf | |||
1cb5dd461b | |||
845599a5e8 | |||
0cc02c292f | |||
1919702326 | |||
0c0052e1cd | |||
78622770ec | |||
7b86727394 | |||
0965f8648e | |||
98974b4367 | |||
597bcadd9e | |||
4d9aabcb91 | |||
1606c2884d | |||
12f69b593f | |||
1ca45f90d0 | |||
a91a5616f9 | |||
c525e09368 | |||
f5bba4a6a0 | |||
77a37fb573 | |||
6b24c5878c | |||
f4414e1249 | |||
b72971f4ce | |||
b9af4e6804 | |||
2fd1d42d1e | |||
3cfc7d7fa9 | |||
b5d9055fcf | |||
63d644d95f | |||
e16192b416 | |||
505e018448 | |||
5ced0e2809 | |||
0e1d919f7e | |||
a009db998e | |||
d6c6bd933b | |||
859cc03f35 | |||
1a3b8ae3b8 | |||
863a08abf3 | |||
fd9c6afa5e | |||
8f3797407b | |||
7eedb23285 | |||
e4a2c95dd8 | |||
9429228b71 | |||
aafbc60f12 | |||
7170611791 | |||
59e57f3dd5 | |||
fd0d25b081 | |||
fa529c911a | |||
dc997b7ef4 | |||
0168d32f96 | |||
d6fc0c779c | |||
08be36edfa | |||
990d287953 | |||
a629d4ab45 | |||
78b48af886 | |||
72d267853c | |||
66a218c2ec | |||
d4f3ec2245 | |||
7bdc19bf4b | |||
103ef788fb | |||
b6f6d1f3cc | |||
ef3d2e4e04 | |||
8ec8b91ead | |||
819a1d473d | |||
c930337255 | |||
57bb4a9d96 | |||
1776341242 | |||
983eec6941 | |||
86d390ee1a | |||
91703409d9 | |||
3322d0e4a5 | |||
d09038fde2 | |||
71966deaac | |||
12e83c9468 |
166
.github/workflows/ci.yaml
vendored
166
.github/workflows/ci.yaml
vendored
@ -2,87 +2,135 @@ name: ci
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- develop
|
||||
- main
|
||||
|
||||
jobs:
|
||||
|
||||
test_node:
|
||||
runs-on: ubuntu-latest
|
||||
test:
|
||||
runs-on: macos-10.15
|
||||
strategy:
|
||||
matrix:
|
||||
node: [ '14', '13', '12' ]
|
||||
name: Test with Node v${{ matrix.node }}
|
||||
steps:
|
||||
- name: Tell if project is using npm or yarn
|
||||
id: _1
|
||||
uses: garronej/github_actions_toolkit@v1.11
|
||||
id: step1
|
||||
uses: garronej/github_actions_toolkit@v2.2
|
||||
with:
|
||||
action_name: tell_if_project_uses_npm_or_yarn
|
||||
owner: ${{github.repository_owner}}
|
||||
repo: ${{github.event.repository.name}}
|
||||
branch: ${{github.ref}}
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
- uses: actions/checkout@v2.3.4
|
||||
- uses: actions/setup-node@v2.1.3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- if: steps._1.outputs.npm_or_yarn == 'yarn'
|
||||
- if: steps.step1.outputs.npm_or_yarn == 'yarn'
|
||||
run: |
|
||||
yarn install --frozen-lockfile
|
||||
yarn run build
|
||||
yarn run test
|
||||
- if: steps._1.outputs.npm_or_yarn == 'npm'
|
||||
yarn build
|
||||
yarn test
|
||||
- if: steps.step1.outputs.npm_or_yarn == 'npm'
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
npm run test
|
||||
trigger_publish:
|
||||
name: Trigger publish.yaml workflow if package.json version updated ( and secrets.PAT is set ).
|
||||
npm test
|
||||
check_if_version_upgraded:
|
||||
name: Check if version upgrade
|
||||
if: github.event_name == 'push'
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PAT: ${{secrets.PAT}}
|
||||
if: github.event_name == 'push' && github.event.head_commit.author.name != 'ts_ci'
|
||||
needs: test_node
|
||||
needs: test
|
||||
outputs:
|
||||
from_version: ${{ steps.step1.outputs.from_version }}
|
||||
to_version: ${{ steps.step1.outputs.to_version }}
|
||||
is_upgraded_version: ${{steps.step1.outputs.is_upgraded_version }}
|
||||
steps:
|
||||
|
||||
- name: Get version on latest
|
||||
id: v_latest
|
||||
uses: garronej/github_actions_toolkit@v1.11
|
||||
- uses: garronej/github_actions_toolkit@v2.2
|
||||
id: step1
|
||||
with:
|
||||
action_name: get_package_json_version
|
||||
owner: ${{github.repository_owner}}
|
||||
repo: ${{github.event.repository.name}}
|
||||
branch: latest
|
||||
compare_to_version: '0.0.0'
|
||||
action_name: is_package_json_version_upgraded
|
||||
|
||||
- name: Get version on develop
|
||||
id: v_develop
|
||||
uses: garronej/github_actions_toolkit@v1.11
|
||||
with:
|
||||
action_name: get_package_json_version
|
||||
owner: ${{github.repository_owner}}
|
||||
repo: ${{github.event.repository.name}}
|
||||
branch: ${{ github.sha }}
|
||||
compare_to_version: ${{steps.v_latest.outputs.version || '0.0.0'}}
|
||||
|
||||
- name: 'Trigger the ''publish'' workflow'
|
||||
if: ${{ !!env.PAT && steps.v_develop.outputs.compare_result == '1' }}
|
||||
uses: garronej/github_actions_toolkit@v1.11
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
||||
update_changelog:
|
||||
runs-on: ubuntu-latest
|
||||
needs: check_if_version_upgraded
|
||||
if: needs.check_if_version_upgraded.outputs.is_upgraded_version == 'true'
|
||||
steps:
|
||||
- uses: garronej/github_actions_toolkit@v2.2
|
||||
with:
|
||||
action_name: dispatch_event
|
||||
owner: ${{github.repository_owner}}
|
||||
repo: ${{github.event.repository.name}}
|
||||
event_type: publish
|
||||
client_payload_json: |
|
||||
${{
|
||||
format(
|
||||
'{{"from_version":"{0}","to_version":"{1}","repo":"{2}"}}',
|
||||
steps.v_latest.outputs.version,
|
||||
steps.v_develop.outputs.version,
|
||||
github.event.repository.name
|
||||
)
|
||||
}}
|
||||
action_name: update_changelog
|
||||
branch: ${{ github.ref }}
|
||||
commit_author_email: ts_ci@github.com
|
||||
|
||||
create_github_release:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- update_changelog
|
||||
- check_if_version_upgraded
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
- name: Build GitHub release body
|
||||
id: step1
|
||||
run: |
|
||||
if [ "$FROM_VERSION" = "0.0.0" ]; then
|
||||
echo "::set-output name=body::🚀"
|
||||
else
|
||||
echo "::set-output name=body::📋 [CHANGELOG](https://github.com/$GITHUB_REPOSITORY/blob/v$TO_VERSION/CHANGELOG.md)"
|
||||
fi
|
||||
env:
|
||||
FROM_VERSION: ${{ needs.check_if_version_upgraded.outputs.from_version }}
|
||||
TO_VERSION: ${{ needs.check_if_version_upgraded.outputs.to_version }}
|
||||
- uses: garronej/action-gh-release@v0.2.0
|
||||
with:
|
||||
name: Release v${{ needs.check_if_version_upgraded.outputs.to_version }}
|
||||
tag_name: v${{ needs.check_if_version_upgraded.outputs.to_version }}
|
||||
target_commitish: ${{ github.ref }}
|
||||
body: ${{ steps.step1.outputs.body }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
publish_on_npm:
|
||||
runs-on: macos-10.15
|
||||
needs:
|
||||
- update_changelog
|
||||
- check_if_version_upgraded
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.4
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
- uses: actions/setup-node@v2.1.3
|
||||
with:
|
||||
node-version: '15'
|
||||
- run: |
|
||||
PACKAGE_MANAGER=npm
|
||||
if [ -f "./yarn.lock" ]; then
|
||||
PACKAGE_MANAGER=yarn
|
||||
fi
|
||||
if [ "$PACKAGE_MANAGER" = "yarn" ]; then
|
||||
yarn install --frozen-lockfile
|
||||
else
|
||||
npm ci
|
||||
fi
|
||||
$PACKAGE_MANAGER run build
|
||||
- run: npx -y -p denoify@0.6.5 denoify_enable_short_npm_import_path
|
||||
env:
|
||||
DRY_RUN: "0"
|
||||
- name: Publishing on NPM
|
||||
run: |
|
||||
if [ "$(npm show . version)" = "$VERSION" ]; then
|
||||
echo "This version is already published"
|
||||
exit 0
|
||||
fi
|
||||
if [ "$NPM_TOKEN" = "" ]; then
|
||||
echo "Can't publish on NPM, You must first create a secret called NPM_TOKEN that contains your NPM auth token. https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets"
|
||||
false
|
||||
fi
|
||||
echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > .npmrc
|
||||
npm publish
|
||||
rm .npmrc
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
VERSION: ${{ needs.check_if_version_upgraded.outputs.to_version }}
|
116
.github/workflows/publish.yaml
vendored
116
.github/workflows/publish.yaml
vendored
@ -1,116 +0,0 @@
|
||||
on:
|
||||
repository_dispatch:
|
||||
types: publish
|
||||
|
||||
jobs:
|
||||
update_changelog_and_sync_package_lock_version:
|
||||
name: Update CHANGELOG.md and make sure package.json and package-lock.json versions matches.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Synchronize package.json and package-lock.json version if needed.
|
||||
uses: garronej/github_actions_toolkit@v1.11
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
||||
with:
|
||||
action_name: sync_package_and_package_lock_version
|
||||
owner: ${{github.repository_owner}}
|
||||
repo: ${{github.event.client_payload.repo}}
|
||||
branch: develop
|
||||
commit_author_email: ts_ci@github.com
|
||||
- name: Update CHANGELOG.md
|
||||
if: ${{ !!github.event.client_payload.from_version }}
|
||||
uses: garronej/github_actions_toolkit@v1.11
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
||||
with:
|
||||
action_name: update_changelog
|
||||
owner: ${{github.repository_owner}}
|
||||
repo: ${{github.event.client_payload.repo}}
|
||||
branch_behind: latest
|
||||
branch_ahead: develop
|
||||
commit_author_email: ts_ci@github.com
|
||||
exclude_commit_from_author_names_json: '["ts_ci"]'
|
||||
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
needs: update_changelog_and_sync_package_lock_version
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: develop
|
||||
- name: Remove .github directory, useless on 'latest' branch
|
||||
run: rm -r .github
|
||||
- name: Remove branch 'latest'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
git branch -d latest || true
|
||||
git push origin :latest
|
||||
- name: Create the new 'latest' branch
|
||||
run: |
|
||||
git branch latest
|
||||
git checkout latest
|
||||
- uses: actions/setup-node@v1
|
||||
- run: |
|
||||
if [ -f "./yarn.lock" ]; then
|
||||
yarn install --frozen-lockfile
|
||||
else
|
||||
npm ci
|
||||
fi
|
||||
- run: |
|
||||
PACKAGE_MANAGER=npm
|
||||
if [ -f "./yarn.lock" ]; then
|
||||
PACKAGE_MANAGER=yarn
|
||||
fi
|
||||
$PACKAGE_MANAGER run enable_short_import_path
|
||||
env:
|
||||
DRY_RUN: "0"
|
||||
- name: (DEBUG) Show how the files have been moved to enable short import
|
||||
run: ls -lR
|
||||
- name: Publishing on NPM
|
||||
run: |
|
||||
if [ "$(npm show . version)" = "$VERSION" ]; then
|
||||
echo "This version is already published"
|
||||
exit 0
|
||||
fi
|
||||
if [ "$NPM_TOKEN" = "" ]; then
|
||||
echo "Can't publish on NPM, You must first create a secret called NPM_TOKEN that contains your NPM auth token. https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets"
|
||||
false
|
||||
fi
|
||||
echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > .npmrc
|
||||
npm publish
|
||||
rm .npmrc
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
VERSION: ${{ github.event.client_payload.to_version }}
|
||||
- name: Commit changes
|
||||
run: |
|
||||
git config --local user.email "ts_ci@github.com"
|
||||
git config --local user.name "ts_ci"
|
||||
git add -A
|
||||
git commit -am "Enabling shorter import paths [automatic]"
|
||||
- run: git push origin latest
|
||||
- name: Release body
|
||||
id: id_rb
|
||||
run: |
|
||||
if [ "$FROM_VERSION" = "" ]; then
|
||||
echo "::set-output name=body::🚀"
|
||||
else
|
||||
echo "::set-output name=body::📋 [CHANGELOG](https://github.com/$OWNER/$REPO/blob/$REF/CHANGELOG.md)"
|
||||
fi
|
||||
env:
|
||||
FROM_VERSION: ${{ github.event.client_payload.from_version }}
|
||||
OWNER: ${{github.repository_owner}}
|
||||
REPO: ${{github.event.client_payload.repo}}
|
||||
REF: v${{github.event.client_payload.to_version}}
|
||||
- name: Create Release
|
||||
uses: garronej/create-release@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PAT }}
|
||||
with:
|
||||
tag_name: v${{ github.event.client_payload.to_version }}
|
||||
release_name: Release v${{ github.event.client_payload.to_version }}
|
||||
branch: latest
|
||||
draft: false
|
||||
prerelease: false
|
||||
body: ${{ steps.id_rb.outputs.body }}
|
153
CHANGELOG.md
153
CHANGELOG.md
@ -1,3 +1,156 @@
|
||||
### **0.3.23** (2021-04-08)
|
||||
|
||||
- Allow to lazily load therms
|
||||
|
||||
### **0.3.22** (2021-04-08)
|
||||
|
||||
- update powerhooks
|
||||
- Support terms and condition
|
||||
- Fix info.ftl
|
||||
- For useKcMessage we prefer returning callbacks with a changing references
|
||||
|
||||
### **0.3.21** (2021-04-04)
|
||||
|
||||
- Update powerhooks
|
||||
|
||||
### **0.3.20** (2021-04-01)
|
||||
|
||||
- Always catch freemarker errors
|
||||
|
||||
### **0.3.19** (2021-04-01)
|
||||
|
||||
- Fix previous release
|
||||
|
||||
### **0.3.18** (2021-04-01)
|
||||
|
||||
- Fix error.ftt, Adopt best effort strategy to convert ftl values into JS
|
||||
|
||||
### **0.3.17** (2021-03-29)
|
||||
|
||||
- Use push instead of replace in keycloak-js adapter to enable going back
|
||||
|
||||
### **0.3.15** (2021-03-28)
|
||||
|
||||
- Remove all reference to --external-assets, broken feature
|
||||
|
||||
### **0.3.14** (2021-03-28)
|
||||
|
||||
- Fix standalone mode: imports from js
|
||||
|
||||
### **0.3.13** (2021-03-26)
|
||||
|
||||
|
||||
|
||||
### **0.3.12** (2021-03-26)
|
||||
|
||||
- Fix mocksContext
|
||||
|
||||
### **0.3.11** (2021-03-26)
|
||||
|
||||
- Fix previous build, improve README
|
||||
|
||||
### **0.3.10** (2021-03-26)
|
||||
|
||||
- Handle <style> tag, improve documentation
|
||||
|
||||
### **0.3.9** (2021-03-25)
|
||||
|
||||
- Update readme
|
||||
- Document --external-assets
|
||||
- Update README.md
|
||||
- Update README.md
|
||||
- Update README.md
|
||||
|
||||
### **0.3.8** (2021-03-22)
|
||||
|
||||
- Make standalone mode the default
|
||||
|
||||
### **0.3.7** (2021-03-22)
|
||||
|
||||
- (test) external asset mode by default
|
||||
|
||||
### **0.3.6** (2021-03-22)
|
||||
|
||||
- Fix previous release
|
||||
|
||||
### **0.3.5** (2021-03-22)
|
||||
|
||||
- support homepage with urlPath
|
||||
|
||||
### **0.3.4** (2021-03-22)
|
||||
|
||||
- Bugfix: Import assets from CSS
|
||||
|
||||
### **0.3.3** (2021-03-22)
|
||||
|
||||
- Fix submit not receving correct text
|
||||
|
||||
### **0.3.2** (2021-03-21)
|
||||
|
||||
- Fix broken previous release
|
||||
|
||||
### **0.3.1** (2021-03-21)
|
||||
|
||||
- kcHeaderClass can be updated after initial mount
|
||||
|
||||
## **0.3.0** (2021-03-20)
|
||||
|
||||
- Bump version
|
||||
- Feat: Cary over states using URL search params
|
||||
- Bugfix: with kcHtmlClass
|
||||
|
||||
### **0.2.10** (2021-03-19)
|
||||
|
||||
- Remove dependency to denoify
|
||||
|
||||
### **0.2.9** (2021-03-19)
|
||||
|
||||
- Update deps and CI workflow
|
||||
|
||||
### **0.2.8** (2021-03-19)
|
||||
|
||||
- Bugfix: keycloak_build that grow and grow in size
|
||||
- Add disclaimer about maitainment strategy
|
||||
- Add a note for tested version support
|
||||
|
||||
### **0.2.7** (2021-03-13)
|
||||
|
||||
- Bump version
|
||||
- Update README.md
|
||||
- Update README.md
|
||||
|
||||
### **0.2.6** (2021-03-10)
|
||||
|
||||
- Fix generated gitignore
|
||||
|
||||
### **0.2.5** (2021-03-10)
|
||||
|
||||
- Fix generated .gitignore
|
||||
|
||||
### **0.2.4** (2021-03-10)
|
||||
|
||||
- Update README.md
|
||||
|
||||
### **0.2.3** (2021-03-09)
|
||||
|
||||
- fix gitignore generation
|
||||
|
||||
### **0.2.2** (2021-03-08)
|
||||
|
||||
- Add table of content
|
||||
- Update README.md
|
||||
- Update README.md
|
||||
|
||||
## **0.2.1** (2021-03-08)
|
||||
|
||||
- Update ci.yaml
|
||||
- Update readme
|
||||
- Update readme
|
||||
- update deps
|
||||
- Update readme
|
||||
- Add all mocks for testing
|
||||
- many small fixes
|
||||
|
||||
### **0.1.6** (2021-03-07)
|
||||
|
||||
- Fix Turkish
|
||||
|
266
README.md
266
README.md
@ -2,7 +2,7 @@
|
||||
<img src="https://user-images.githubusercontent.com/6702424/109387840-eba11f80-7903-11eb-9050-db1dad883f78.png">
|
||||
</p>
|
||||
<p align="center">
|
||||
<i>🔏 Keycloak theme generator for Reacts app 🔏</i>
|
||||
<i>🔏 Customize key cloak's pages as if they were part of your App 🔏</i>
|
||||
<br>
|
||||
<br>
|
||||
<img src="https://github.com/garronej/keycloakify/workflows/ci/badge.svg?branch=develop">
|
||||
@ -11,78 +11,266 @@
|
||||
<img src="https://img.shields.io/npm/l/keycloakify">
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
**!!! This module is under active developement. It is not usable yet!!!**
|
||||
<p align="center">
|
||||
<i>Ultimately this build tool Generates a Keycloak theme</i>
|
||||
<img src="https://user-images.githubusercontent.com/6702424/110260457-a1c3d380-7fac-11eb-853a-80459b65626b.png">
|
||||
</p>
|
||||
|
||||
# Motivations
|
||||
|
||||
The problem:
|
||||
|
||||

|
||||
|
||||
<p align="center">
|
||||
<i>Without keycloakify:</i><br>
|
||||
<img src="https://user-images.githubusercontent.com/6702424/108838381-dbbbcf80-75d3-11eb-8ae8-db41563ef9db.gif">
|
||||
</p>
|
||||
When we redirected to Keycloak the user suffers from a harsh context switch.
|
||||
On je login/register pages the language is set back to default and the theme is different that the one on the app.
|
||||
|
||||
Keycloak does offer a way to customize theses pages but it requires a lot of raw HTML/CSS hacking
|
||||
to reproduce the look and feel of a specific app. Not mentioning the maintenance cost of such an endeavour.
|
||||
|
||||
Wouldn't it be great if we could just design the login and register pages as if they where part of our app while
|
||||
still letting Keycloak handle the heavy lifting of actually authenticating the users?
|
||||
|
||||
Wouldn't it be great if we could just design the login and register pages as if they where part of our app?
|
||||
Here is `yarn add keycloakify` for you 🍸
|
||||
|
||||
TODO: Insert video after.
|
||||
<p align="center">
|
||||
<i>With keycloakify:</i><br>
|
||||
<img src="https://github.com/InseeFrLab/keycloakify/releases/download/v0.3.8/keycloakify_after.gif">
|
||||
</p>
|
||||
|
||||
**TL;DR**: [Here](https://github.com/garronej/keycloakify-demo-app) is a Hello World React project with Keycloakify set up.
|
||||
|
||||
- [Motivations](#motivations)
|
||||
- [How to use](#how-to-use)
|
||||
- [Setting up the build tool](#setting-up-the-build-tool)
|
||||
- [Developing your login and register pages in your React app](#developing-your-login-and-register-pages-in-your-react-app)
|
||||
- [Just changing the look](#just-changing-the-look)
|
||||
- [Changing the look **and** feel](#changing-the-look-and-feel)
|
||||
- [Hot reload](#hot-reload)
|
||||
- [GitHub Actions](#github-actions)
|
||||
- [Requirements](#requirements)
|
||||
- [Limitations](#limitations)
|
||||
- [`process.env.PUBLIC_URL` not supported.](#processenvpublic_url-not-supported)
|
||||
- [`@font-face` importing fonts from the `src/` dir](#font-face-importing-fonts-from-thesrc-dir)
|
||||
- [Example of setup that **won't** work](#example-of-setup-that-wont-work)
|
||||
- [Workarounds](#workarounds)
|
||||
- [Implement context persistence (optional)](#implement-context-persistence-optional)
|
||||
- [API Reference](#api-reference)
|
||||
- [The build tool](#the-build-tool)
|
||||
|
||||
# How to use
|
||||
|
||||
## Setting up the build tool
|
||||
|
||||
Add `keycloakify` to the dev dependencies of your project `npm install --save-dev keycloakify` or `yarn add --dev keycloakify`
|
||||
then configure your `package.json` build's script to build the keycloak's theme by adding `&& build-keycloak-theme`.
|
||||
|
||||
Typically you will get:
|
||||
|
||||
`package.json`:
|
||||
[`package.json`](https://github.com/garronej/keycloakify-demo-app/blob/main/package.json)
|
||||
```json
|
||||
"devDependencies": {
|
||||
"homepage": "https://URL.OF/YOUR-APP"
|
||||
"dependencies": {
|
||||
"keycloakify": "^0.0.10"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "react-scripts build && build-keycloak-theme"
|
||||
},
|
||||
},
|
||||
"scripts": {
|
||||
"keycloak": "yarn build && build-keycloak-theme",
|
||||
},
|
||||
```
|
||||
`"homepage"` must be specified only if the url path is not `/`
|
||||
(Onl `/YOUR-APP` matters `URL.OF` don't have to be the actual domain)
|
||||
|
||||
Then build your app with `yarn run build` or `npm run build`, you will be provided with instructions
|
||||
about how to load the theme into Keycloak.
|
||||
It is mandatory that you specify the url where your app will be available
|
||||
using the `homepage` field.
|
||||
|
||||
[](https://youtu.be/xTz0Rj7i2v8)
|
||||
Once you've edited your `package.json` you can install your new
|
||||
dependency with `yarn install` and build the keycloak theme with
|
||||
`yarn keycloak`.
|
||||
|
||||
Once the build is complete instructions about how to load
|
||||
the theme into Keycloak are printed in the console.
|
||||
|
||||
## Developing your login and register pages in your React app
|
||||
|
||||
TODO
|
||||
### Just changing the look
|
||||
|
||||
# How to implement context persistance
|
||||
The first approach is to only arr/replace the default class names by your
|
||||
own.
|
||||
|
||||
If you want dark mode preference, language and others users preferences your can do so
|
||||
very easily by using [`powerhooks/useGlobalState`](https://github.com/garronej/powerhooks)
|
||||
```tsx
|
||||
|
||||
WARNING: `powerhooks` is still a work in progress.
|
||||
import { App } from "./<wherever>/App";
|
||||
import {
|
||||
KcApp,
|
||||
defaultKcProps,
|
||||
kcContext
|
||||
} from "keycloakify";
|
||||
import { css } from "tss-react";
|
||||
|
||||
# REQUIREMENTS
|
||||
const myClassName = css({ "color": "red" });
|
||||
|
||||
reactDom.render(
|
||||
// Unless the app is currently being served by Keycloak
|
||||
// kcContext is undefined.
|
||||
kcContext !== undefined ?
|
||||
<KcApp
|
||||
kcContext={kcContext}
|
||||
{...{
|
||||
...defaultKcProps,
|
||||
"kcHeaderWrapperClass": myClassName
|
||||
}}
|
||||
/> :
|
||||
<App />, // Your actual app
|
||||
document.getElementById("root")
|
||||
);
|
||||
```
|
||||
|
||||
<i>result:</i>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://user-images.githubusercontent.com/6702424/110261408-688d6280-7fb0-11eb-9822-7003d268b459.png">
|
||||
</p>
|
||||
|
||||
### Changing the look **and** feel
|
||||
|
||||
If you want to really re-implement the pages the best approach is to
|
||||
create you own version of the [`<KcApp />`](https://github.com/garronej/keycloakify/blob/develop/src/lib/components/KcApp.tsx).
|
||||
Copy/past some of [the components](https://github.com/garronej/keycloakify/tree/develop/src/lib/components) provided by this module and start hacking around.
|
||||
|
||||
### Hot reload
|
||||
|
||||
By default, in order to see your changes you will have to wait for
|
||||
`yarn build` to complete which can takes sevrall minute.
|
||||
|
||||
If you want to test your login screens outside of Keycloak, in [storybook](https://storybook.js.org/)
|
||||
for example you can use `kcContextMocks`.
|
||||
|
||||
```tsx
|
||||
import {
|
||||
KcApp,
|
||||
defaultKcProps,
|
||||
kcContextMocks
|
||||
} from "keycloakify";
|
||||
|
||||
reactDom.render(
|
||||
kcContext !== undefined ?
|
||||
<KcApp
|
||||
kcContext={kcContextMocks.kcLoginContext}
|
||||
{...defaultKcProps}
|
||||
/>
|
||||
document.getElementById("root")
|
||||
);
|
||||
```
|
||||
|
||||
then `yarn start` ...
|
||||
|
||||
Checkout [this concrete example](https://github.com/garronej/keycloakify-demo-app/blob/main/src/index.tsx)
|
||||
|
||||
|
||||
*NOTE: keycloak-react-theming was renamed keycloakify since this video was recorded*
|
||||
[](https://youtu.be/xTz0Rj7i2v8)
|
||||
|
||||
# GitHub Actions
|
||||
|
||||

|
||||
|
||||
[Here is a demo repo](https://github.com/garronej/keycloakify-demo-app) to show how to automate
|
||||
the building and publishing of the theme (the .jar file).
|
||||
|
||||
# Requirements
|
||||
|
||||
Tested with the following Keycloak versions:
|
||||
- [11.0.3](https://hub.docker.com/layers/jboss/keycloak/11.0.3/images/sha256-4438f1e51c1369371cb807dffa526e1208086b3ebb9cab009830a178de949782?context=explore)
|
||||
- [12.0.4](https://hub.docker.com/layers/jboss/keycloak/12.0.4/images/sha256-67e0c88e69bd0c7aef972c40bdeb558a974013a28b3668ca790ed63a04d70584?context=explore)
|
||||
|
||||
This tool will be maintained to stay compatible with Keycloak v11 and up, however, the default pages you will get
|
||||
(before you customize it) will always be the ones of the Keycloak v11.
|
||||
|
||||
This tools assumes you are bundling your app with Webpack (tested with 4.44.2) .
|
||||
It assumes there is a `build/` directory at the root of your react project directory containing a `index.html` file
|
||||
and a `static/` directory generated by webpack.
|
||||
and a `build/static/` directory generated by webpack.
|
||||
|
||||
**All this is defaults with [`create-react-app`](https://create-react-app.dev)** (tested with 4.0.3=)
|
||||
|
||||
- For building the theme: `mvn` (Maven) must be installed
|
||||
- For development, (testing the theme in a local container ): `rm`, `mkdir`, `wget`, `unzip` are assumed to be available
|
||||
- For development (testing the theme in a local container ): `rm`, `mkdir`, `wget`, `unzip` are assumed to be available
|
||||
and `docker` up and running.
|
||||
|
||||
NOTE: This build tool has only be tested on MacOS.
|
||||
|
||||
# Limitations
|
||||
|
||||
## `process.env.PUBLIC_URL` not supported.
|
||||
|
||||
You won't be able to [import things from your public directory in your JavaScript code](https://create-react-app.dev/docs/using-the-public-folder/#adding-assets-outside-of-the-module-system). (This isn't recommended anyway).
|
||||
|
||||
## `@font-face` importing fonts from the `src/` dir
|
||||
### Example of setup that **won't** work
|
||||
|
||||
- We have a `fonts/` directory in `src/`
|
||||
- We import the font like this [`src: url("/fonts/my-font.woff2") format("woff2");`(https://github.com/garronej/keycloakify-demo-app/blob/07d54a3012ef354ee12b1374c6f7ad1cb125d56b/src/fonts.scss#L4) in a `.scss` a file.
|
||||
|
||||
### Workarounds
|
||||
|
||||
If it is possible, use Google Fonts or any other font provider.
|
||||
|
||||
If you want to host your font recommended approach is to move your fonts into the `public`
|
||||
directory and to place your `@font-face` statements in the `public/index.html`.
|
||||
Example [here]().
|
||||
|
||||
You can also [use your explicit url](https://github.com/garronej/keycloakify-demo-app/blob/2de8a9eb6f5de9c94f9cd3991faad0377e63268c/src/fonts.scss#L16) but don't forget [`Access-Control-Allow-Origin`](https://github.com/garronej/keycloakify-demo-app/blob/2de8a9eb6f5de9c94f9cd3991faad0377e63268c/nginx.conf#L17-L19).
|
||||
|
||||
# Implement context persistence (optional)
|
||||
|
||||
If, before logging in, a user has selected a specific language
|
||||
you don't want it to be reset to default when the user gets redirected to
|
||||
the login or register pages.
|
||||
|
||||
Same goes for the dark mode, you don't want, if the user had it enabled
|
||||
to show the login page with light themes.
|
||||
|
||||
The problem is that you are probably using `localStorage` to persist theses values across
|
||||
reload but, as the Keycloak pages are not served on the same domain that the rest of your
|
||||
app you won't be able to carry over states using `localStorage`.
|
||||
|
||||
The only reliable solution is to inject parameters into the URL before
|
||||
redirecting to Keycloak. We integrate with
|
||||
[`keycloak-js`](https://github.com/keycloak/keycloak-documentation/blob/master/securing_apps/topics/oidc/javascript-adapter.adoc),
|
||||
by providing you a way to tell `keycloak-js` that you would like to inject
|
||||
some search parameters before redirecting.
|
||||
|
||||
The method also works with [`@react-keycloak/web`](https://www.npmjs.com/package/@react-keycloak/web) (use the `initOptions`).
|
||||
|
||||
You can implement your own mechanism to pass the states in the URL and
|
||||
restore it on the other side but we recommend using `powerhooks/useGlobalState`
|
||||
from the library [`powerhooks`](https://www.powerhooks.dev) that provide an elegant
|
||||
way to handle states such as `isDarkModeEnabled` or `selectedLanguage`.
|
||||
|
||||
Let's modify [the example](https://github.com/keycloak/keycloak-documentation/blob/master/securing_apps/topics/oidc/javascript-adapter.adoc) from the official `keycloak-js` documentation to
|
||||
enables the states of `useGlobalStates` to be injected in the URL before redirecting.
|
||||
Note that the states are automatically restored on the other side by `powerhooks`
|
||||
|
||||
```typescript
|
||||
import keycloak_js from "keycloak-js";
|
||||
import { injectGlobalStatesInSearchParams } from "powerhooks/useGlobalState";
|
||||
import { createKeycloakAdapter } from "keycloakify";
|
||||
|
||||
//...
|
||||
|
||||
const keycloakInstance = keycloak_js({
|
||||
"url": "http://keycloak-server/auth",
|
||||
"realm": "myrealm",
|
||||
"clientId": "myapp"
|
||||
});
|
||||
|
||||
keycloakInstance.init({
|
||||
"onLoad": 'check-sso',
|
||||
"silentCheckSsoRedirectUri": window.location.origin + "/silent-check-sso.html",
|
||||
"adapter": createKeycloakAdapter({
|
||||
"transformUrlBeforeRedirect": injectGlobalStatesInSearchParams,
|
||||
keycloakInstance
|
||||
})
|
||||
});
|
||||
|
||||
//...
|
||||
```
|
||||
|
||||
If you really want to go the extra miles and avoid having the white
|
||||
flash of the blank html before the js bundle have been evaluated
|
||||
[here is a snippet](https://github.com/InseeFrLab/onyxia-ui/blob/a77eb502870cfe6878edd0d956c646d28746d053/public/index.html#L5-L54) that you can place in your `public/index.html` if you are using `powerhooks/useGlobalState`.
|
||||
|
||||
# API Reference
|
||||
|
||||
## The build tool
|
||||
@ -90,10 +278,4 @@ NOTE: This build tool has only be tested on MacOS.
|
||||
Part of the lib that runs with node, at build time.
|
||||
|
||||
- `npx build-keycloak-theme`: Builds the theme, the CWD is assumed to be the root of your react project.
|
||||
- `npx download-sample-keycloak-themes`: Downloads the keycloak default themes (for development purposes)
|
||||
|
||||
## The fronted lib ( imported into your react app )
|
||||
|
||||
Part of the lib that you import in your react project and runs on the browser.
|
||||
|
||||
**TODO**
|
||||
- `npx download-sample-keycloak-themes`: Downloads the keycloak default themes (for development purposes)
|
14
package.json
14
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "keycloakify",
|
||||
"version": "0.1.6",
|
||||
"version": "0.3.23",
|
||||
"description": "Keycloak theme generator for Reacts app",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -13,10 +13,8 @@
|
||||
"build": "yarn clean && tsc && yarn grant-exec-perms && yarn copy-files",
|
||||
"grant-exec-perms": "node dist/bin/tools/grant-exec-perms.js",
|
||||
"test": "node dist/test",
|
||||
"enable_short_import_path": "yarn build && denoify_enable_short_npm_import_path",
|
||||
"copy-files": "copyfiles -u 1 src/**/*.ftl src/**/*.xml src/**/*.js dist/",
|
||||
"generate-messages": "node dist/bin/generate-i18n-messages.js",
|
||||
"watch": "tsc -w"
|
||||
"generate-messages": "node dist/bin/generate-i18n-messages.js"
|
||||
},
|
||||
"bin": {
|
||||
"build-keycloak-theme": "dist/bin/build-keycloak-theme/index.js",
|
||||
@ -45,18 +43,18 @@
|
||||
"@types/node": "^10.0.0",
|
||||
"@types/react": "^17.0.0",
|
||||
"copyfiles": "^2.4.1",
|
||||
"denoify": "^0.6.5",
|
||||
"properties-parser": "^0.3.1",
|
||||
"react": "^17.0.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"typescript": "^4.1.5"
|
||||
"typescript": "^4.2.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"cheerio": "^1.0.0-rc.5",
|
||||
"evt": "2.0.0-beta.15",
|
||||
"minimal-polyfills": "^2.1.6",
|
||||
"powerhooks": "^0.0.19",
|
||||
"path": "^0.12.7",
|
||||
"powerhooks": "^0.0.36",
|
||||
"scripting-tools": "^0.19.13",
|
||||
"tss-react": "^0.0.11"
|
||||
"tss-react": "^0.0.12"
|
||||
}
|
||||
}
|
||||
|
263
src/bin/build-keycloak-theme/generateFtl/common.ftl
Normal file
263
src/bin/build-keycloak-theme/generateFtl/common.ftl
Normal file
@ -0,0 +1,263 @@
|
||||
<script>const _=
|
||||
{
|
||||
"url": {
|
||||
"loginAction": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${url.loginAction?no_esc}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"resourcesPath": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${url.resourcesPath?no_esc}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"resourcesCommonPath": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${url.resourcesCommonPath?no_esc}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"loginRestartFlowUrl": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${url.loginRestartFlowUrl?no_esc}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"loginUrl": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${url.loginUrl?no_esc}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})()
|
||||
},
|
||||
"realm": {
|
||||
"displayName": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${realm.displayName!''}" || undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"displayNameHtml": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${realm.displayNameHtml!''}" || undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"internationalizationEnabled": (function (){
|
||||
|
||||
<#attempt>
|
||||
return ${realm.internationalizationEnabled?c};
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"registrationEmailAsUsername": (function (){
|
||||
|
||||
<#attempt>
|
||||
return ${realm.registrationEmailAsUsername?c};
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})()
|
||||
},
|
||||
"locale": (function (){
|
||||
|
||||
<#attempt>
|
||||
<#if realm.internationalizationEnabled>
|
||||
|
||||
return {
|
||||
"supported": (function(){
|
||||
|
||||
var out= [];
|
||||
|
||||
<#attempt>
|
||||
<#list locale.supported as lng>
|
||||
out.push({
|
||||
"url": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${lng.url?no_esc}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"label": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${lng.label}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"languageTag": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${lng.languageTag}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})()
|
||||
});
|
||||
</#list>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
return out;
|
||||
|
||||
})(),
|
||||
"current": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${locale.current}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})()
|
||||
};
|
||||
|
||||
</#if>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"auth": (function (){
|
||||
|
||||
<#attempt>
|
||||
<#if auth?has_content>
|
||||
|
||||
var out= {
|
||||
"showUsername": (function (){
|
||||
|
||||
<#attempt>
|
||||
return ${auth.showUsername()?c};
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"showResetCredentials": (function (){
|
||||
|
||||
<#attempt>
|
||||
return ${auth.showResetCredentials()?c};
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"showTryAnotherWayLink": (function(){
|
||||
|
||||
<#attempt>
|
||||
return ${auth.showTryAnotherWayLink()?c};
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})()
|
||||
};
|
||||
|
||||
<#attempt>
|
||||
<#if auth.showUsername() && !auth.showResetCredentials()>
|
||||
Object.assign(
|
||||
out,
|
||||
{
|
||||
"attemptedUsername": (function (){
|
||||
<#attempt>
|
||||
return "${auth.attemptedUsername}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
})()
|
||||
}
|
||||
);
|
||||
</#if>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
return out;
|
||||
|
||||
</#if>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"scripts": (function(){
|
||||
|
||||
var out = [];
|
||||
|
||||
<#attempt>
|
||||
<#if scripts??>
|
||||
<#attempt>
|
||||
<#list scripts as script>
|
||||
out.push((function (){
|
||||
|
||||
<#attempt>
|
||||
return "${script}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})());
|
||||
</#list>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
</#if>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
return out;
|
||||
|
||||
})(),
|
||||
"message": (function (){
|
||||
|
||||
<#attempt>
|
||||
<#if message?has_content>
|
||||
|
||||
return {
|
||||
"type": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${message.type}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"summary": (function (){
|
||||
|
||||
<#attempt>
|
||||
return String.htmlUnescape("${message.summary}");
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})()
|
||||
};
|
||||
|
||||
</#if>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"isAppInitiatedAction": (function (){
|
||||
|
||||
<#attempt>
|
||||
<#if isAppInitiatedAction??>
|
||||
return true;
|
||||
</#if>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
return false;
|
||||
|
||||
})()
|
||||
}
|
||||
</script>
|
@ -2,13 +2,21 @@
|
||||
{
|
||||
"client": (function (){
|
||||
|
||||
<#attempt>
|
||||
<#if client??>
|
||||
return {
|
||||
"baseUrl": "${(client.baseUrl!'')?no_esc}" || undefined
|
||||
"baseUrl": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${(client.baseUrl!'')?no_esc}" || undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})()
|
||||
};
|
||||
</#if>
|
||||
|
||||
return undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})()
|
||||
}
|
||||
|
@ -2,23 +2,28 @@
|
||||
|
||||
import cheerio from "cheerio";
|
||||
import {
|
||||
replaceImportFromStaticInJsCode,
|
||||
replaceImportsFromStaticInJsCode,
|
||||
replaceImportsInInlineCssCode,
|
||||
generateCssCodeToDefineGlobals
|
||||
} from "../replaceImportFromStatic";
|
||||
import fs from "fs";
|
||||
import { join as pathJoin } from "path";
|
||||
import { objectKeys } from "evt/tools/typeSafety/objectKeys";
|
||||
|
||||
export const pageIds = ["login.ftl", "register.ftl", "info.ftl", "error.ftl", "login-reset-password.ftl", "login-verify-email.ftl"] as const;
|
||||
export const pageIds = [
|
||||
"login.ftl", "register.ftl", "info.ftl",
|
||||
"error.ftl", "login-reset-password.ftl",
|
||||
"login-verify-email.ftl", "terms.ftl"
|
||||
] as const;
|
||||
|
||||
export type PageId = typeof pageIds[number];
|
||||
export type PageId = typeof pageIds[number];
|
||||
|
||||
function loadAdjacentFile(fileBasename: string){
|
||||
function loadAdjacentFile(fileBasename: string) {
|
||||
return fs.readFileSync(pathJoin(__dirname, fileBasename))
|
||||
.toString("utf8");
|
||||
};
|
||||
|
||||
function loadFtlFile(ftlFileBasename: PageId | "template.ftl") {
|
||||
function loadFtlFile(ftlFileBasename: PageId | "common.ftl") {
|
||||
try {
|
||||
|
||||
return loadAdjacentFile(ftlFileBasename)
|
||||
@ -29,21 +34,23 @@ function loadFtlFile(ftlFileBasename: PageId | "template.ftl") {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function generateFtlFilesCodeFactory(
|
||||
params: {
|
||||
ftlValuesGlobalName: string;
|
||||
cssGlobalsToDefine: Record<string, string>;
|
||||
indexHtmlCode: string;
|
||||
urlPathname: string;
|
||||
}
|
||||
) {
|
||||
|
||||
const { ftlValuesGlobalName, cssGlobalsToDefine, indexHtmlCode } = params;
|
||||
const { ftlValuesGlobalName, cssGlobalsToDefine, indexHtmlCode, urlPathname } = params;
|
||||
|
||||
const $ = cheerio.load(indexHtmlCode);
|
||||
|
||||
$("script:not([src])").each((...[, element]) => {
|
||||
|
||||
const { fixedJsCode } = replaceImportFromStaticInJsCode({
|
||||
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
|
||||
ftlValuesGlobalName,
|
||||
"jsCode": $(element).html()!
|
||||
});
|
||||
@ -52,6 +59,17 @@ export function generateFtlFilesCodeFactory(
|
||||
|
||||
});
|
||||
|
||||
$("style").each((...[, element]) => {
|
||||
|
||||
const { fixedCssCode } = replaceImportsInInlineCssCode({
|
||||
"cssCode": $(element).html()!,
|
||||
"urlPathname": params.urlPathname
|
||||
});
|
||||
|
||||
$(element).text(fixedCssCode);
|
||||
|
||||
});
|
||||
|
||||
([
|
||||
["link", "href"],
|
||||
["script", "src"],
|
||||
@ -60,18 +78,24 @@ export function generateFtlFilesCodeFactory(
|
||||
|
||||
const href = $(element).attr(attrName);
|
||||
|
||||
if (!href?.startsWith("/")) {
|
||||
if (href === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(element).attr(attrName, "${url.resourcesPath}/build" + href);
|
||||
$(element).attr(
|
||||
attrName,
|
||||
href.replace(
|
||||
new RegExp(`^${urlPathname.replace(/\//g, "\\/")}`),
|
||||
"${url.resourcesPath}/build/"
|
||||
)
|
||||
);
|
||||
|
||||
})
|
||||
);
|
||||
|
||||
//FTL is no valid html, we can't insert with cheerio, we put placeholder for injecting later.
|
||||
const ftlCommonPlaceholders = {
|
||||
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': loadFtlFile("template.ftl"),
|
||||
'{ "x": "vIdLqMeOed9sdLdIdOxdK0d" }': loadFtlFile("common.ftl"),
|
||||
'<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->':
|
||||
[
|
||||
'<#if scripts??>',
|
||||
@ -89,9 +113,10 @@ export function generateFtlFilesCodeFactory(
|
||||
...(Object.keys(cssGlobalsToDefine).length === 0 ? [] : [
|
||||
'',
|
||||
'<style>',
|
||||
generateCssCodeToDefineGlobals(
|
||||
{ cssGlobalsToDefine }
|
||||
).cssCodeToPrependInHead,
|
||||
generateCssCodeToDefineGlobals({
|
||||
cssGlobalsToDefine,
|
||||
urlPathname
|
||||
}).cssCodeToPrependInHead,
|
||||
'</style>',
|
||||
''
|
||||
]),
|
||||
|
@ -1,37 +1,82 @@
|
||||
<script>const _=
|
||||
{
|
||||
"messageHeader": "${messageHeader!''}" || undefined,
|
||||
"messageHeader": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${messageHeader!''}" || undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"requiredActions": (function (){
|
||||
|
||||
<#attempt>
|
||||
<#if requiredActions??>
|
||||
|
||||
var out =[];
|
||||
|
||||
<#attempt>
|
||||
<#list requiredActions>
|
||||
<#attempt>
|
||||
<#items as reqActionItem>
|
||||
out.push("${reqActionItem}");
|
||||
</#items></b>
|
||||
out.push((function (){
|
||||
|
||||
<#attempt>
|
||||
return "${reqActionItem}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})());
|
||||
</#items>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
</#list>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
return out;
|
||||
|
||||
<#else>
|
||||
|
||||
return undefined;
|
||||
</#if>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"skipLink": (function (){
|
||||
|
||||
<#attempt>
|
||||
<#if skipLink??>
|
||||
return true;
|
||||
</#if>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
return false;
|
||||
|
||||
})(),
|
||||
"pageRedirectUri": "${(pageRedirectUri!'')?no_esc}" || undefined,
|
||||
"actionUri": "${(actionUri!'')?no_esc}" || undefined,
|
||||
"pageRedirectUri": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${(pageRedirectUri!'')?no_esc}" || undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"actionUri": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${(actionUri!'')?no_esc}" || undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"client": {
|
||||
"baseUrl": "${(client.baseUrl!'')?no_esc}" || undefined
|
||||
"baseUrl": (function(){
|
||||
|
||||
<#attempt>
|
||||
return "${(client.baseUrl!'')?no_esc}" || undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})()
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,7 +1,14 @@
|
||||
<script>const _=
|
||||
{
|
||||
"realm": {
|
||||
"loginWithEmailAllowed": ${realm.loginWithEmailAllowed?c}
|
||||
},
|
||||
"loginWithEmailAllowed": (function (){
|
||||
|
||||
<#attempt>
|
||||
return ${realm.loginWithEmailAllowed?c};
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})()
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,83 +1,160 @@
|
||||
<script>const _=
|
||||
{
|
||||
"url": {
|
||||
"loginResetCredentialsUrl": "${url.loginResetCredentialsUrl?no_esc}",
|
||||
"registrationUrl": "${url.registrationUrl?no_esc}"
|
||||
"loginResetCredentialsUrl": (function (){
|
||||
<#attempt>
|
||||
return "${url.loginResetCredentialsUrl?no_esc}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
})(),
|
||||
"registrationUrl": (function (){
|
||||
<#attempt>
|
||||
return "${url.registrationUrl?no_esc}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
})()
|
||||
},
|
||||
"realm": {
|
||||
"loginWithEmailAllowed": ${realm.loginWithEmailAllowed?c},
|
||||
"rememberMe": ${realm.rememberMe?c},
|
||||
"password": ${realm.password?c},
|
||||
"resetPasswordAllowed": ${realm.resetPasswordAllowed?c},
|
||||
"registrationAllowed": ${realm.registrationAllowed?c}
|
||||
"loginWithEmailAllowed": (function(){
|
||||
<#attempt>
|
||||
return ${realm.loginWithEmailAllowed?c};
|
||||
<#recover>
|
||||
</#attempt>
|
||||
})(),
|
||||
"rememberMe": (function (){
|
||||
<#attempt>
|
||||
return ${realm.rememberMe?c};
|
||||
<#recover>
|
||||
</#attempt>
|
||||
})(),
|
||||
"password": (function (){
|
||||
<#attempt>
|
||||
return ${realm.password?c};
|
||||
<#recover>
|
||||
</#attempt>
|
||||
})(),
|
||||
"resetPasswordAllowed": (function (){
|
||||
<#attempt>
|
||||
return ${realm.resetPasswordAllowed?c};
|
||||
<#recover>
|
||||
</#attempt>
|
||||
})(),
|
||||
"registrationAllowed": (function (){
|
||||
<#attempt>
|
||||
return ${realm.registrationAllowed?c};
|
||||
<#recover>
|
||||
</#attempt>
|
||||
})()
|
||||
},
|
||||
"auth": (function (){
|
||||
|
||||
<#attempt>
|
||||
<#if auth?has_content>
|
||||
|
||||
var out= {
|
||||
"selectedCredential": "${auth.selectedCredential!''}" || undefined
|
||||
return {
|
||||
"selectedCredential": (function (){
|
||||
<#attempt>
|
||||
return "${auth.selectedCredential!''}" || undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
})()
|
||||
};
|
||||
|
||||
return out;
|
||||
|
||||
</#if>
|
||||
|
||||
return undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"social": {
|
||||
"displayInfo": ${social.displayInfo?c},
|
||||
"displayInfo": (function (){
|
||||
<#attempt>
|
||||
return ${social.displayInfo?c};
|
||||
<#recover>
|
||||
</#attempt>
|
||||
})(),
|
||||
"providers": (()=>{
|
||||
|
||||
<#attempt>
|
||||
<#if social.providers??>
|
||||
|
||||
var out= [];
|
||||
|
||||
<#attempt>
|
||||
<#list social.providers as p>
|
||||
out.push({
|
||||
"loginUrl": "${p.loginUrl?no_esc}",
|
||||
"alias": "${p.alias}",
|
||||
"providerId": "${p.providerId}",
|
||||
"displayName": "${p.displayName}"
|
||||
"loginUrl": (function (){
|
||||
<#attempt>
|
||||
return "${p.loginUrl?no_esc}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
})(),
|
||||
"alias": (function (){
|
||||
<#attempt>
|
||||
return "${p.alias}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
})(),
|
||||
"providerId": (function (){
|
||||
<#attempt>
|
||||
return "${p.providerId}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
})(),
|
||||
"displayName": (function (){
|
||||
<#attempt>
|
||||
return "${p.displayName}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
})()
|
||||
});
|
||||
</#list>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
return out;
|
||||
|
||||
</#if>
|
||||
|
||||
return undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})()
|
||||
},
|
||||
"usernameEditDisabled": (function () {
|
||||
|
||||
<#attempt>
|
||||
<#if usernameEditDisabled??>
|
||||
return true;
|
||||
</#if>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
return false;
|
||||
|
||||
})(),
|
||||
"login": {
|
||||
"username": "${login.username!''}" || undefined,
|
||||
"username": (function (){
|
||||
<#attempt>
|
||||
return "${login.username!''}" || undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
})(),
|
||||
"rememberMe": (function (){
|
||||
|
||||
<#attempt>
|
||||
<#if login.rememberMe??>
|
||||
return true;
|
||||
</#if>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
return false;
|
||||
|
||||
|
||||
})()
|
||||
},
|
||||
"registrationDisabled": (function (){
|
||||
|
||||
<#attempt>
|
||||
<#if registrationDisabled??>
|
||||
return true;
|
||||
</#if>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
return false;
|
||||
|
||||
})()
|
||||
}
|
||||
</script>
|
@ -1,46 +1,152 @@
|
||||
<script>const _=
|
||||
{
|
||||
"url": {
|
||||
"registrationAction": "${url.registrationAction?no_esc}"
|
||||
"registrationAction": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${url.registrationAction?no_esc}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})()
|
||||
},
|
||||
"messagesPerField": {
|
||||
"printIfExists": function (key, x) {
|
||||
switch(key){
|
||||
case "userLabel": "${messagesPerField.printIfExists('userLabel','1')}" ? x : undefined;
|
||||
case "username": "${messagesPerField.printIfExists('username','1')}" ? x : undefined;
|
||||
case "email": "${messagesPerField.printIfExists('email','1')}" ? x : undefined;
|
||||
case "firstName": "${messagesPerField.printIfExists('firstName','1')}" ? x : undefined;
|
||||
case "lastName": "${messagesPerField.printIfExists('lastName','1')}" ? x : undefined;
|
||||
case "password": "${messagesPerField.printIfExists('password','1')}" ? x : undefined;
|
||||
case "password-confirm": "${messagesPerField.printIfExists('password-confirm','1')}" ? x : undefined;
|
||||
case "userLabel": return (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${messagesPerField.printIfExists('userLabel','1')}" ? x : undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})();
|
||||
case "username": return (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${messagesPerField.printIfExists('username','1')}" ? x : undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})();
|
||||
case "email": return (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${messagesPerField.printIfExists('email','1')}" ? x : undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})();
|
||||
case "firstName": return (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${messagesPerField.printIfExists('firstName','1')}" ? x : undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})();
|
||||
case "lastName": return (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${messagesPerField.printIfExists('lastName','1')}" ? x : undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})();
|
||||
case "password": return (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${messagesPerField.printIfExists('password','1')}" ? x : undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})();
|
||||
case "password-confirm": return (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${messagesPerField.printIfExists('password-confirm','1')}" ? x : undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})();
|
||||
}
|
||||
}
|
||||
},
|
||||
"register": {
|
||||
"formData": {
|
||||
"firstName": "${register.formData.firstName!''}" || undefined,
|
||||
"displayName": "${register.formData.displayName!''}" || undefined,
|
||||
"lastName": "${register.formData.lastName!''}" || undefined,
|
||||
"email": "${register.formData.email!''}" || undefined,
|
||||
"username": "${register.formData.username!''}" || undefined
|
||||
"firstName": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${register.formData.firstName!''}" || undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"displayName": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${register.formData.displayName!''}" || undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"lastName": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${register.formData.lastName!''}" || undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"email": (function(){
|
||||
|
||||
<#attempt>
|
||||
return "${register.formData.email!''}" || undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"username": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${register.formData.username!''}" || undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})()
|
||||
}
|
||||
},
|
||||
"passwordRequired": (function (){
|
||||
|
||||
<#attempt>
|
||||
<#if passwordRequired??>
|
||||
return true;
|
||||
</#if>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
return false;
|
||||
|
||||
})(),
|
||||
"recaptchaRequired": (function (){
|
||||
|
||||
<#attempt>
|
||||
<#if passwordRequired??>
|
||||
return true;
|
||||
</#if>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
return false;
|
||||
|
||||
})(),
|
||||
"recaptchaSiteKey": "${recaptchaSiteKey!''}" || undefined
|
||||
"recaptchaSiteKey": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${recaptchaSiteKey!''}" || undefined;
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})()
|
||||
}
|
||||
</script>
|
@ -1,114 +0,0 @@
|
||||
<script>const _=
|
||||
{
|
||||
"url": {
|
||||
"loginAction": "${url.loginAction?no_esc}",
|
||||
"resourcesPath": "${url.resourcesPath?no_esc}",
|
||||
"resourcesCommonPath": "${url.resourcesCommonPath?no_esc}",
|
||||
"loginRestartFlowUrl": "${url.loginRestartFlowUrl?no_esc}",
|
||||
"loginUrl": "${url.loginUrl?no_esc}"
|
||||
},
|
||||
"realm": {
|
||||
"displayName": "${realm.displayName!''}" || undefined,
|
||||
"displayNameHtml": "${realm.displayNameHtml!''}" || undefined,
|
||||
"internationalizationEnabled": ${realm.internationalizationEnabled?c},
|
||||
"registrationEmailAsUsername": ${realm.registrationEmailAsUsername?c},
|
||||
},
|
||||
"locale": (function (){
|
||||
|
||||
<#if realm.internationalizationEnabled>
|
||||
|
||||
return {
|
||||
"supported": (function(){
|
||||
|
||||
<#if realm.internationalizationEnabled>
|
||||
|
||||
var out= [];
|
||||
|
||||
<#list locale.supported as lng>
|
||||
out.push({
|
||||
"url": "${lng.url?no_esc}",
|
||||
"label": "${lng.label}",
|
||||
"languageTag": "${lng.languageTag}"
|
||||
});
|
||||
</#list>
|
||||
|
||||
return out;
|
||||
|
||||
</#if>
|
||||
|
||||
return undefined;
|
||||
|
||||
})(),
|
||||
"current": "${locale.current}"
|
||||
};
|
||||
|
||||
</#if>
|
||||
|
||||
return undefined;
|
||||
|
||||
})(),
|
||||
"auth": (function (){
|
||||
|
||||
|
||||
<#if auth?has_content>
|
||||
|
||||
var out= {
|
||||
"showUsername": ${auth.showUsername()?c},
|
||||
"showResetCredentials": ${auth.showResetCredentials()?c},
|
||||
"showTryAnotherWayLink": ${auth.showTryAnotherWayLink()?c},
|
||||
};
|
||||
|
||||
<#if auth.showUsername() && !auth.showResetCredentials()>
|
||||
Object.assign(
|
||||
out,
|
||||
{
|
||||
"attemptedUsername": "${auth.attemptedUsername}"
|
||||
}
|
||||
);
|
||||
</#if>
|
||||
|
||||
return out;
|
||||
|
||||
</#if>
|
||||
|
||||
|
||||
return undefined;
|
||||
|
||||
})(),
|
||||
"scripts": (function(){
|
||||
|
||||
var out = [];
|
||||
|
||||
<#if scripts??>
|
||||
<#list scripts as script>
|
||||
out.push("${script}");
|
||||
</#list>
|
||||
</#if>
|
||||
|
||||
return out;
|
||||
|
||||
})(),
|
||||
"message": (function (){
|
||||
|
||||
<#if message?has_content>
|
||||
|
||||
return {
|
||||
"type": "${message.type}",
|
||||
"summary": String.htmlUnescape("${message.summary}")
|
||||
};
|
||||
|
||||
</#if>
|
||||
|
||||
return undefined;
|
||||
|
||||
})(),
|
||||
"isAppInitiatedAction": (function (){
|
||||
|
||||
<#if isAppInitiatedAction??>
|
||||
return true;
|
||||
</#if>
|
||||
return false;
|
||||
|
||||
})()
|
||||
}
|
||||
</script>
|
@ -3,24 +3,28 @@ import { transformCodebase } from "../tools/transformCodebase";
|
||||
import * as fs from "fs";
|
||||
import { join as pathJoin } from "path";
|
||||
import {
|
||||
replaceImportFromStaticInCssCode,
|
||||
replaceImportFromStaticInJsCode
|
||||
replaceImportsInCssCode,
|
||||
replaceImportsFromStaticInJsCode
|
||||
} from "./replaceImportFromStatic";
|
||||
import { generateFtlFilesCodeFactory, pageIds } from "./generateFtl";
|
||||
import { builtinThemesUrl } from "../install-builtin-keycloak-themes";
|
||||
import { downloadAndUnzip } from "../tools/downloadAndUnzip";
|
||||
import { builtinThemesUrl } from "../install-builtin-keycloak-themes";
|
||||
import { downloadAndUnzip } from "../tools/downloadAndUnzip";
|
||||
import * as child_process from "child_process";
|
||||
import { ftlValuesGlobalName } from "./ftlValuesGlobalName";
|
||||
import { ftlValuesGlobalName } from "./ftlValuesGlobalName";
|
||||
import { resourcesCommonPath, resourcesPath, subDirOfPublicDirBasename } from "../../lib/kcContextMocks/urlResourcesPath";
|
||||
import { isInside } from "../tools/isInside";
|
||||
|
||||
|
||||
export function generateKeycloakThemeResources(
|
||||
params: {
|
||||
themeName: string;
|
||||
reactAppBuildDirPath: string;
|
||||
keycloakThemeBuildingDirPath: string;
|
||||
urlPathname: string;
|
||||
}
|
||||
) {
|
||||
|
||||
const { themeName, reactAppBuildDirPath, keycloakThemeBuildingDirPath } = params;
|
||||
const { themeName, reactAppBuildDirPath, keycloakThemeBuildingDirPath, urlPathname } = params;
|
||||
|
||||
const themeDirPath = pathJoin(keycloakThemeBuildingDirPath, "src", "main", "resources", "theme", themeName, "login");
|
||||
|
||||
@ -31,9 +35,20 @@ export function generateKeycloakThemeResources(
|
||||
"srcDirPath": reactAppBuildDirPath,
|
||||
"transformSourceCode": ({ filePath, sourceCode }) => {
|
||||
|
||||
//NOTE: Prevent cycles, excludes the folder we generated for debug in public/
|
||||
if (
|
||||
isInside({
|
||||
"dirPath": pathJoin(reactAppBuildDirPath, subDirOfPublicDirBasename),
|
||||
filePath
|
||||
})
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
if (/\.css?$/i.test(filePath)) {
|
||||
|
||||
const { cssGlobalsToDefine, fixedCssCode } = replaceImportFromStaticInCssCode(
|
||||
const { cssGlobalsToDefine, fixedCssCode } = replaceImportsInCssCode(
|
||||
{ "cssCode": sourceCode.toString("utf8") }
|
||||
);
|
||||
|
||||
@ -46,9 +61,10 @@ export function generateKeycloakThemeResources(
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (/\.js?$/i.test(filePath)) {
|
||||
|
||||
const { fixedJsCode } = replaceImportFromStaticInJsCode({
|
||||
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
|
||||
"jsCode": sourceCode.toString("utf8"),
|
||||
ftlValuesGlobalName
|
||||
});
|
||||
@ -67,7 +83,8 @@ export function generateKeycloakThemeResources(
|
||||
ftlValuesGlobalName,
|
||||
"indexHtmlCode": fs.readFileSync(
|
||||
pathJoin(reactAppBuildDirPath, "index.html")
|
||||
).toString("utf8")
|
||||
).toString("utf8"),
|
||||
urlPathname
|
||||
});
|
||||
|
||||
pageIds.forEach(pageId => {
|
||||
@ -90,11 +107,48 @@ export function generateKeycloakThemeResources(
|
||||
"destDirPath": tmpDirPath
|
||||
});
|
||||
|
||||
const themeResourcesDirPath = pathJoin(themeDirPath, "resources");
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": pathJoin(tmpDirPath, "keycloak", "login", "resources"),
|
||||
"destDirPath": pathJoin(themeDirPath, "resources")
|
||||
"destDirPath": themeResourcesDirPath
|
||||
});
|
||||
|
||||
const reactAppPublicDirPath = pathJoin(reactAppBuildDirPath, "..", "public");
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": themeResourcesDirPath,
|
||||
"destDirPath": pathJoin(
|
||||
reactAppPublicDirPath,
|
||||
resourcesPath
|
||||
)
|
||||
});
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": pathJoin(tmpDirPath, "keycloak", "common", "resources"),
|
||||
"destDirPath": pathJoin(
|
||||
reactAppPublicDirPath,
|
||||
resourcesCommonPath
|
||||
)
|
||||
});
|
||||
|
||||
const keycloakResourcesWithinPublicDirPath =
|
||||
pathJoin(reactAppPublicDirPath, subDirOfPublicDirBasename);
|
||||
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(keycloakResourcesWithinPublicDirPath, "README.txt"),
|
||||
Buffer.from([
|
||||
"This is just a test folder that helps develop",
|
||||
"the login and register page without having to yarn build"
|
||||
].join(" "))
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(keycloakResourcesWithinPublicDirPath, ".gitignore"),
|
||||
Buffer.from("*", "utf8")
|
||||
);
|
||||
|
||||
child_process.execSync(`rm -r ${tmpDirPath}`);
|
||||
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import type { ParsedPackageJson } from "./generateJavaStackFiles";
|
||||
import { join as pathJoin, relative as pathRelative, basename as pathBasename } from "path";
|
||||
import * as child_process from "child_process";
|
||||
import { generateDebugFiles, containerLaunchScriptBasename } from "./generateDebugFiles";
|
||||
import { URL } from "url";
|
||||
|
||||
|
||||
const reactProjectDirPath = process.cwd();
|
||||
@ -22,7 +23,25 @@ if (require.main === module) {
|
||||
generateKeycloakThemeResources({
|
||||
keycloakThemeBuildingDirPath,
|
||||
"reactAppBuildDirPath": pathJoin(reactProjectDirPath, "build"),
|
||||
"themeName": parsedPackageJson.name
|
||||
"themeName": parsedPackageJson.name,
|
||||
"urlPathname": (() => {
|
||||
|
||||
const url = (() => {
|
||||
|
||||
const { homepage } = parsedPackageJson;
|
||||
|
||||
return homepage === undefined ?
|
||||
undefined :
|
||||
new URL(homepage);
|
||||
|
||||
})();
|
||||
|
||||
return url === undefined ?
|
||||
"/" :
|
||||
url.pathname.replace(/([^/])$/, "$1/");
|
||||
|
||||
|
||||
})()
|
||||
});
|
||||
|
||||
const { jarFilePath } = generateJavaStackFiles({
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
import * as crypto from "crypto";
|
||||
|
||||
export function replaceImportFromStaticInJsCode(
|
||||
export function replaceImportsFromStaticInJsCode(
|
||||
params: {
|
||||
ftlValuesGlobalName: string;
|
||||
jsCode: string;
|
||||
@ -10,16 +10,36 @@ export function replaceImportFromStaticInJsCode(
|
||||
|
||||
const { jsCode, ftlValuesGlobalName } = params;
|
||||
|
||||
const fixedJsCode = jsCode!.replace(
|
||||
/"static\//g,
|
||||
`window.${ftlValuesGlobalName}.url.resourcesPath.replace(/^\\//,"") + "/build/static/`
|
||||
const fixedJsCode = jsCode.replace(
|
||||
/[a-z]+\.[a-z]+\+"static\//g,
|
||||
`window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/`
|
||||
);
|
||||
|
||||
return { fixedJsCode };
|
||||
|
||||
}
|
||||
|
||||
export function replaceImportFromStaticInCssCode(
|
||||
export function replaceImportsInInlineCssCode(
|
||||
params: {
|
||||
cssCode: string;
|
||||
urlPathname: string;
|
||||
}
|
||||
): { fixedCssCode: string; } {
|
||||
|
||||
const { cssCode, urlPathname } = params;
|
||||
|
||||
const fixedCssCode = cssCode.replace(
|
||||
urlPathname === "/" ?
|
||||
/url\(\/([^/][^)]+)\)/g :
|
||||
new RegExp(`url\\(${urlPathname}([^)]+)\\)`, "g"),
|
||||
(...[, group]) => `url(${"${url.resourcesPath}/build/" + group})`
|
||||
);
|
||||
|
||||
return { fixedCssCode };
|
||||
|
||||
}
|
||||
|
||||
export function replaceImportsInCssCode(
|
||||
params: {
|
||||
cssCode: string;
|
||||
}
|
||||
@ -32,7 +52,7 @@ export function replaceImportFromStaticInCssCode(
|
||||
|
||||
const cssGlobalsToDefine: Record<string, string> = {};
|
||||
|
||||
new Set(cssCode.match(/(url\(\/[^)]+\))/g) ?? [])
|
||||
new Set(cssCode.match(/url\(\/[^/][^)]+\)[^;}]*/g) ?? [])
|
||||
.forEach(match =>
|
||||
cssGlobalsToDefine[
|
||||
"url" + crypto
|
||||
@ -60,12 +80,13 @@ export function replaceImportFromStaticInCssCode(
|
||||
export function generateCssCodeToDefineGlobals(
|
||||
params: {
|
||||
cssGlobalsToDefine: Record<string, string>;
|
||||
urlPathname: string;
|
||||
}
|
||||
): {
|
||||
cssCodeToPrependInHead: string;
|
||||
} {
|
||||
|
||||
const { cssGlobalsToDefine } = params;
|
||||
const { cssGlobalsToDefine, urlPathname } = params;
|
||||
|
||||
return {
|
||||
"cssCodeToPrependInHead": [
|
||||
@ -73,12 +94,8 @@ export function generateCssCodeToDefineGlobals(
|
||||
...Object.keys(cssGlobalsToDefine)
|
||||
.map(cssVariableName => [
|
||||
`--${cssVariableName}:`,
|
||||
[
|
||||
"url(",
|
||||
"${url.resourcesPath}/build" +
|
||||
cssGlobalsToDefine[cssVariableName].match(/^url\(([^)]+)\)$/)![1],
|
||||
")"
|
||||
].join("")
|
||||
cssGlobalsToDefine[cssVariableName]
|
||||
.replace(new RegExp(`url\\(${urlPathname.replace(/\//g, "\\/")}`, "g"), "url(${url.resourcesPath}/build/")
|
||||
].join(" "))
|
||||
.map(line => ` ${line};`),
|
||||
"}"
|
||||
|
@ -49,7 +49,7 @@ crawl(".").forEach(filePath => {
|
||||
|
||||
child_process.execSync(`rm -r ${tmpDirPath}`);
|
||||
|
||||
const targetDirPath = pathJoin(getProjectRoot(), "src", "lib", "i18n", "generated_messages");
|
||||
const targetDirPath = pathJoin(getProjectRoot(), "src", "lib", "i18n", "generated_kcMessages");
|
||||
|
||||
fs.mkdirSync(targetDirPath, { "recursive": true });
|
||||
|
||||
@ -65,7 +65,7 @@ Object.keys(record).forEach(pageType => {
|
||||
'//PLEASE DO NOT EDIT MANUALLY',
|
||||
'',
|
||||
'/* spell-checker: disable */',
|
||||
`export const messages= ${JSON.stringify(record[pageType], null, 2)} as const;`,
|
||||
`export const kcMessages= ${JSON.stringify(record[pageType], null, 2)};`,
|
||||
'/* spell-checker: enable */'
|
||||
].join("\n"), "utf8")
|
||||
);
|
||||
|
14
src/bin/tools/isInside.ts
Normal file
14
src/bin/tools/isInside.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { relative as pathRelative } from "path";
|
||||
|
||||
export function isInside(
|
||||
params: {
|
||||
dirPath: string;
|
||||
filePath: string;
|
||||
}
|
||||
) {
|
||||
|
||||
const { dirPath, filePath } = params;
|
||||
|
||||
return !pathRelative(dirPath, filePath).startsWith("..");
|
||||
|
||||
}
|
@ -3,24 +3,20 @@ import { memo } from "react";
|
||||
import { Template } from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import { assert } from "../tools/assert";
|
||||
import { kcContext } from "../kcContext";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import { useKcMessage } from "../i18n/useKcMessage";
|
||||
|
||||
export const Error = memo((props: KcProps) => {
|
||||
export const Error = memo(({ kcContext, ...props }: { kcContext: KcContext.Error; } & KcProps) => {
|
||||
|
||||
const { msg } = useKcMessage();
|
||||
|
||||
assert(
|
||||
kcContext !== undefined &&
|
||||
kcContext.pageId === "error.ftl" &&
|
||||
kcContext.message !== undefined
|
||||
);
|
||||
assert(kcContext.message !== undefined);
|
||||
|
||||
const { message, client } = kcContext;
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...props}
|
||||
{...{ kcContext, ...props }}
|
||||
displayMessage={false}
|
||||
headerNode={msg("errorTitle")}
|
||||
formNode={
|
||||
|
@ -3,18 +3,14 @@ import { memo } from "react";
|
||||
import { Template } from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import { assert } from "../tools/assert";
|
||||
import { kcContext } from "../kcContext";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import { useKcMessage } from "../i18n/useKcMessage";
|
||||
|
||||
export const Info = memo((props: KcProps) => {
|
||||
export const Info = memo(({ kcContext, ...props }: { kcContext: KcContext.Info; } & KcProps) => {
|
||||
|
||||
const { msg } = useKcMessage();
|
||||
|
||||
assert(
|
||||
kcContext !== undefined &&
|
||||
kcContext.pageId === "info.ftl" &&
|
||||
kcContext.message !== undefined
|
||||
);
|
||||
assert(kcContext.message !== undefined);
|
||||
|
||||
const {
|
||||
messageHeader,
|
||||
@ -28,7 +24,7 @@ export const Info = memo((props: KcProps) => {
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...props}
|
||||
{...{ kcContext, ...props }}
|
||||
displayMessage={false}
|
||||
headerNode={
|
||||
messageHeader !== undefined ?
|
||||
@ -57,13 +53,13 @@ export const Info = memo((props: KcProps) => {
|
||||
{
|
||||
!skipLink &&
|
||||
pageRedirectUri !== undefined ?
|
||||
<p><a href="${pageRedirectUri}">${(msg("backToApplication"))}</a></p>
|
||||
<p><a href={pageRedirectUri}>{(msg("backToApplication"))}</a></p>
|
||||
:
|
||||
actionUri !== undefined ?
|
||||
<p><a href="${actionUri}">${msg("proceedWithAction")}</a></p>
|
||||
<p><a href={actionUri}>{msg("proceedWithAction")}</a></p>
|
||||
:
|
||||
client.baseUrl !== undefined &&
|
||||
<p><a href="${client.baseUrl}">${msg("backToApplication")}</a></p>
|
||||
<p><a href={client.baseUrl}>{msg("backToApplication")}</a></p>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
|
||||
import { memo } from "react";
|
||||
import { kcContext } from "../kcContext";
|
||||
import { assert } from "../tools/assert";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import { Login } from "./Login";
|
||||
import { Register } from "./Register";
|
||||
@ -9,18 +8,16 @@ import { Info } from "./Info";
|
||||
import { Error } from "./Error";
|
||||
import { LoginResetPassword } from "./LoginResetPassword";
|
||||
import { LoginVerifyEmail } from "./LoginVerifyEmail";
|
||||
import { Terms } from "./Terms";
|
||||
|
||||
export const KcApp = memo((props: KcProps) => {
|
||||
|
||||
assert(kcContext !== undefined, "App is not currently served by a Keycloak server");
|
||||
|
||||
export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContext; } & KcProps ) => {
|
||||
switch (kcContext.pageId) {
|
||||
case "login.ftl": return <Login {...props} />;
|
||||
case "register.ftl": return <Register {...props} />;
|
||||
case "info.ftl": return <Info {...props} />;
|
||||
case "error.ftl": return <Error {...props} />;
|
||||
case "login-reset-password.ftl": return <LoginResetPassword {...props} />;
|
||||
case "login-verify-email.ftl": return <LoginVerifyEmail {...props} />;
|
||||
case "login.ftl": return <Login {...{ kcContext, ...props }} />;
|
||||
case "register.ftl": return <Register {...{ kcContext, ...props }} />;
|
||||
case "info.ftl": return <Info {...{ kcContext, ...props }} />;
|
||||
case "error.ftl": return <Error {...{ kcContext, ...props }} />;
|
||||
case "login-reset-password.ftl": return <LoginResetPassword {...{ kcContext, ...props }} />;
|
||||
case "login-verify-email.ftl": return <LoginVerifyEmail {...{ kcContext, ...props }} />;
|
||||
case "terms.ftl": return <Terms {...{ kcContext, ...props }}/>;
|
||||
}
|
||||
|
||||
});
|
@ -2,21 +2,15 @@
|
||||
import { useState, memo } from "react";
|
||||
import { Template } from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import { assert } from "../tools/assert";
|
||||
import { kcContext } from "../kcContext";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import { useKcMessage } from "../i18n/useKcMessage";
|
||||
import { cx } from "tss-react";
|
||||
import { useConstCallback } from "powerhooks";
|
||||
|
||||
export const Login = memo((props: KcProps) => {
|
||||
export const Login = memo(({ kcContext, ...props }: { kcContext: KcContext.Login; } & KcProps) => {
|
||||
|
||||
const { msg, msgStr } = useKcMessage();
|
||||
|
||||
assert(
|
||||
kcContext !== undefined &&
|
||||
kcContext.pageId === "login.ftl"
|
||||
);
|
||||
|
||||
const {
|
||||
social, realm, url,
|
||||
usernameEditDisabled, login,
|
||||
@ -31,7 +25,7 @@ export const Login = memo((props: KcProps) => {
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...props}
|
||||
{...{ kcContext, ...props }}
|
||||
displayInfo={social.displayInfo}
|
||||
displayWide={realm.password && social.providers !== undefined}
|
||||
headerNode={msg("doLogIn")}
|
||||
@ -68,7 +62,7 @@ export const Login = memo((props: KcProps) => {
|
||||
name="username"
|
||||
defaultValue={login.username ?? ''}
|
||||
type="text"
|
||||
{...(usernameEditDisabled ? { "disabled": true } : { "autoFocus": true, "autocomplete": "off" })}
|
||||
{...(usernameEditDisabled ? { "disabled": true } : { "autoFocus": true, "autoComplete": "off" })}
|
||||
/>
|
||||
</div>
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
|
@ -2,20 +2,14 @@
|
||||
import { memo } from "react";
|
||||
import { Template } from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import { assert } from "../tools/assert";
|
||||
import { kcContext } from "../kcContext";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import { useKcMessage } from "../i18n/useKcMessage";
|
||||
import { cx } from "tss-react";
|
||||
|
||||
export const LoginResetPassword = memo((props: KcProps) => {
|
||||
export const LoginResetPassword = memo(({ kcContext, ...props }: { kcContext: KcContext.LoginResetPassword; } & KcProps) => {
|
||||
|
||||
const { msg, msgStr } = useKcMessage();
|
||||
|
||||
assert(
|
||||
kcContext !== undefined &&
|
||||
kcContext.pageId === "login-reset-password.ftl"
|
||||
);
|
||||
|
||||
const {
|
||||
url,
|
||||
realm,
|
||||
@ -24,7 +18,7 @@ export const LoginResetPassword = memo((props: KcProps) => {
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...props}
|
||||
{...{ kcContext, ...props }}
|
||||
displayMessage={false}
|
||||
headerNode={msg("emailForgotTitle")}
|
||||
formNode={
|
||||
@ -72,7 +66,7 @@ export const LoginResetPassword = memo((props: KcProps) => {
|
||||
props.kcButtonBlockClass, props.kcButtonLargeClass
|
||||
)}
|
||||
type="submit"
|
||||
defaultValue={msgStr("doSubmit")}
|
||||
value={msgStr("doSubmit")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,26 +2,20 @@
|
||||
import { memo } from "react";
|
||||
import { Template } from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import { assert } from "../tools/assert";
|
||||
import { kcContext } from "../kcContext";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import { useKcMessage } from "../i18n/useKcMessage";
|
||||
|
||||
export const LoginVerifyEmail = memo((props: KcProps) => {
|
||||
export const LoginVerifyEmail = memo(({ kcContext, ...props }: { kcContext: KcContext.LoginVerifyEmail; } & KcProps) => {
|
||||
|
||||
const { msg } = useKcMessage();
|
||||
|
||||
assert(
|
||||
kcContext !== undefined &&
|
||||
kcContext.pageId === "login-verify-email.ftl"
|
||||
);
|
||||
|
||||
const {
|
||||
url
|
||||
} = kcContext;
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...props}
|
||||
{...{ kcContext, ...props }}
|
||||
displayMessage={false}
|
||||
headerNode={msg("emailVerifyTitle")}
|
||||
formNode={
|
||||
|
@ -1,20 +1,14 @@
|
||||
import { memo } from "react";
|
||||
import { Template } from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import { assert } from "../tools/assert";
|
||||
import { kcContext } from "../kcContext";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import { useKcMessage } from "../i18n/useKcMessage";
|
||||
import { cx } from "tss-react";
|
||||
|
||||
export const Register = memo((props: KcProps) => {
|
||||
export const Register = memo(({ kcContext, ...props }: { kcContext: KcContext.Register; } & KcProps) => {
|
||||
|
||||
const { msg, msgStr } = useKcMessage();
|
||||
|
||||
assert(
|
||||
kcContext !== undefined &&
|
||||
kcContext.pageId === "register.ftl"
|
||||
);
|
||||
|
||||
const {
|
||||
url,
|
||||
messagesPerField,
|
||||
@ -27,7 +21,7 @@ export const Register = memo((props: KcProps) => {
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...props}
|
||||
{...{ kcContext, ...props }}
|
||||
headerNode={msg("registerTitle")}
|
||||
formNode={
|
||||
<form id="kc-register-form" className={cx(props.kcFormClass)} action={url.registrationAction} method="post">
|
||||
@ -119,7 +113,7 @@ export const Register = memo((props: KcProps) => {
|
||||
|
||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
||||
<input className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonBlockClass, props.kcButtonLargeClass)} type="submit"
|
||||
defaultValue={msgStr("doRegister")} />
|
||||
value={msgStr("doRegister")} />
|
||||
</div>
|
||||
</div>
|
||||
</form >
|
||||
|
@ -3,7 +3,7 @@ import { useReducer, useEffect, memo } from "react";
|
||||
import type { ReactNode } from "react";
|
||||
import { useKcMessage } from "../i18n/useKcMessage";
|
||||
import { useKcLanguageTag } from "../i18n/useKcLanguageTag";
|
||||
import { kcContext } from "../kcContext";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import { assert } from "../tools/assert";
|
||||
import { cx } from "tss-react";
|
||||
import type { KcLanguageTag } from "../i18n/KcLanguageTag";
|
||||
@ -25,8 +25,7 @@ export type TemplateProps = {
|
||||
showUsernameNode?: ReactNode;
|
||||
formNode: ReactNode;
|
||||
infoNode?: ReactNode;
|
||||
} & KcTemplateProps;
|
||||
|
||||
} & { kcContext: KcContext; } & KcTemplateProps;
|
||||
|
||||
export const Template = memo((props: TemplateProps) => {
|
||||
|
||||
@ -39,7 +38,8 @@ export const Template = memo((props: TemplateProps) => {
|
||||
headerNode,
|
||||
showUsernameNode = null,
|
||||
formNode,
|
||||
infoNode = null
|
||||
infoNode = null,
|
||||
kcContext
|
||||
} = props;
|
||||
|
||||
useEffect(() => { console.log("Rendering this page with react using keycloakify") }, []);
|
||||
@ -58,37 +58,36 @@ export const Template = memo((props: TemplateProps) => {
|
||||
(document.forms["kc-select-try-another-way-form" as never].submit(), false)
|
||||
);
|
||||
|
||||
assert(kcContext !== undefined);
|
||||
|
||||
const {
|
||||
realm, locale, auth,
|
||||
realm, locale, auth,
|
||||
url, message, isAppInitiatedAction
|
||||
}= kcContext;
|
||||
} = kcContext;
|
||||
|
||||
useEffect(()=>{
|
||||
useEffect(() => {
|
||||
|
||||
if( !realm.internationalizationEnabled ){
|
||||
if (!realm.internationalizationEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert( locale !== undefined );
|
||||
assert(locale !== undefined);
|
||||
|
||||
if( kcLanguageTag === getBestMatchAmongKcLanguageTag(locale.current) ){
|
||||
if (kcLanguageTag === getBestMatchAmongKcLanguageTag(locale.current)) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.href =
|
||||
window.location.href =
|
||||
locale.supported.find(({ languageTag }) => languageTag === kcLanguageTag)!.url;
|
||||
|
||||
},[kcLanguageTag]);
|
||||
}, [kcLanguageTag]);
|
||||
|
||||
const [isExtraCssLoaded, setExtraCssLoaded] = useReducer(() => true, false);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
let isUnmounted = false;
|
||||
const cleanups: (() => void)[] = [];
|
||||
|
||||
const toArr = (x: string | readonly string[] | undefined) =>
|
||||
const toArr = (x: string | readonly string[] | undefined) =>
|
||||
typeof x === "string" ? x.split(" ") : x ?? [];
|
||||
|
||||
Promise.all(
|
||||
@ -115,13 +114,29 @@ export const Template = memo((props: TemplateProps) => {
|
||||
})
|
||||
);
|
||||
|
||||
document.getElementsByTagName("html")[0]
|
||||
.classList
|
||||
.add(cx(props.kcHtmlClass));
|
||||
if (props.kcHtmlClass !== undefined) {
|
||||
|
||||
return () => { isUnmounted = true; };
|
||||
const htmlClassList =
|
||||
document.getElementsByTagName("html")[0]
|
||||
.classList;
|
||||
|
||||
}, []);
|
||||
const tokens = cx(props.kcHtmlClass).split(" ")
|
||||
|
||||
htmlClassList.add(...tokens);
|
||||
|
||||
cleanups.push(() => htmlClassList.remove(...tokens));
|
||||
|
||||
}
|
||||
|
||||
return () => {
|
||||
|
||||
isUnmounted = true;
|
||||
|
||||
cleanups.forEach(f => f());
|
||||
|
||||
};
|
||||
|
||||
}, [props.kcHtmlClass]);
|
||||
|
||||
if (!isExtraCssLoaded) {
|
||||
return null;
|
||||
@ -154,7 +169,7 @@ export const Template = memo((props: TemplateProps) => {
|
||||
{
|
||||
locale.supported.map(
|
||||
({ languageTag }) =>
|
||||
<li className="kc-dropdown-item">
|
||||
<li key={languageTag} className="kc-dropdown-item">
|
||||
<a href="#" onClick={onChangeLanguageClickFactory(languageTag)}>
|
||||
{getKcLanguageTagLabel(languageTag)}
|
||||
</a>
|
||||
@ -219,21 +234,21 @@ export const Template = memo((props: TemplateProps) => {
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{showUsernameNode}
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<div id="kc-username">
|
||||
<label id="kc-attempted-username">{auth?.attemptedUsername}</label>
|
||||
<a id="reset-login" href={url.loginRestartFlowUrl}>
|
||||
<div className="kc-login-tooltip">
|
||||
<i className={cx(props.kcResetFlowIcon)}></i>
|
||||
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<>
|
||||
{showUsernameNode}
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<div id="kc-username">
|
||||
<label id="kc-attempted-username">{auth?.attemptedUsername}</label>
|
||||
<a id="reset-login" href={url.loginRestartFlowUrl}>
|
||||
<div className="kc-login-tooltip">
|
||||
<i className={cx(props.kcResetFlowIcon)}></i>
|
||||
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
)
|
||||
}
|
||||
</header>
|
||||
|
57
src/lib/components/Terms.tsx
Normal file
57
src/lib/components/Terms.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
import { memo } from "react";
|
||||
import { Template } from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import { useKcMessage } from "../i18n/useKcMessage";
|
||||
import { cx } from "tss-react";
|
||||
|
||||
export const Terms = memo(({ kcContext, ...props }: { kcContext: KcContext.Terms; } & KcProps) => {
|
||||
|
||||
const { msg, msgStr } = useKcMessage();
|
||||
|
||||
const { url } = kcContext;
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, ...props }}
|
||||
displayMessage={false}
|
||||
headerNode={msg("termsTitle")}
|
||||
formNode={
|
||||
<>
|
||||
<div id="kc-terms-text">
|
||||
{msg("termsText")}
|
||||
</div>
|
||||
<form className="form-actions" action={url.loginAction} method="POST">
|
||||
<input
|
||||
className={cx(
|
||||
props.kcButtonClass,
|
||||
props.kcButtonClass,
|
||||
props.kcButtonClass,
|
||||
props.kcButtonPrimaryClass,
|
||||
props.kcButtonLargeClass
|
||||
)}
|
||||
name="accept"
|
||||
id="kc-accept"
|
||||
type="submit"
|
||||
value={msgStr("doAccept")}
|
||||
/>
|
||||
<input
|
||||
className={cx(
|
||||
props.kcButtonClass,
|
||||
props.kcButtonDefaultClass,
|
||||
props.kcButtonLargeClass
|
||||
)}
|
||||
name="cancel"
|
||||
id="kc-decline"
|
||||
type="submit"
|
||||
value={msgStr("doDecline")}
|
||||
/>
|
||||
</form>
|
||||
<div className="clearfix" />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
|
||||
import { objectKeys } from "evt/tools/typeSafety/objectKeys";
|
||||
import { messages } from "./generated_messages/login";
|
||||
import { kcMessages } from "./kcMessages/login";
|
||||
|
||||
export type KcLanguageTag = keyof typeof messages;
|
||||
export type KcLanguageTag = keyof typeof kcMessages;
|
||||
|
||||
export type LanguageLabel =
|
||||
/* spell-checker: disable */
|
||||
@ -40,7 +40,7 @@ export function getKcLanguageTagLabel(language: KcLanguageTag): LanguageLabel {
|
||||
|
||||
}
|
||||
|
||||
const availableLanguages = objectKeys(messages);
|
||||
const availableLanguages = objectKeys(kcMessages);
|
||||
|
||||
/**
|
||||
* Pass in "fr-FR" or "français" for example, it will return the AvailableLanguage
|
||||
|
@ -2,7 +2,7 @@
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
export const messages= {
|
||||
export const kcMessages= {
|
||||
"ca": {
|
||||
"doSave": "Desa",
|
||||
"doCancel": "Cancel·la",
|
||||
@ -3060,5 +3060,5 @@ export const messages= {
|
||||
"locale_ru": "Русский",
|
||||
"locale_zh-CN": "中文简体"
|
||||
}
|
||||
} as const;
|
||||
};
|
||||
/* spell-checker: enable */
|
@ -2,7 +2,7 @@
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
export const messages= {
|
||||
export const kcMessages= {
|
||||
"ca": {
|
||||
"invalidPasswordHistoryMessage": "Contrasenya incorrecta: no pot ser igual a cap de les últimes {0} contrasenyes.",
|
||||
"invalidPasswordMinDigitsMessage": "Contraseña incorrecta: debe contener al menos {0} caracteres numéricos.",
|
||||
@ -240,5 +240,5 @@ export const messages= {
|
||||
"pairwiseFailedToGetRedirectURIs": "无法从服务器获得重定向URL",
|
||||
"pairwiseRedirectURIsMismatch": "客户端的重定向URI与服务器端获取的URI配置不匹配。"
|
||||
}
|
||||
} as const;
|
||||
};
|
||||
/* spell-checker: enable */
|
@ -2,7 +2,7 @@
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
export const messages= {
|
||||
export const kcMessages= {
|
||||
"ca": {
|
||||
"emailVerificationSubject": "Verificació d'email",
|
||||
"emailVerificationBody": "Algú ha creat un compte de {2} amb aquesta adreça de correu electrònic. Si has estat tu, fes clic a l'enllaç següent per verificar la teva adreça de correu electrònic.\n\n{0}\n\nAquest enllaç expirarà en {1} minuts.\n\nSi tu no has creat aquest compte, simplement ignora aquest missatge.",
|
||||
@ -634,5 +634,5 @@ export const messages= {
|
||||
"eventUpdateTotpBody": "您账户的OTP 配置在{0} 由 {1}更改. 如非本人操作,请联系管理员。",
|
||||
"eventUpdateTotpBodyHtml": "<p>您账户的OTP 配置在{0} 由 {1}更改. 如非本人操作,请联系管理员。</p>"
|
||||
}
|
||||
} as const;
|
||||
};
|
||||
/* spell-checker: enable */
|
@ -2,7 +2,7 @@
|
||||
//PLEASE DO NOT EDIT MANUALLY
|
||||
|
||||
/* spell-checker: disable */
|
||||
export const messages= {
|
||||
export const kcMessages= {
|
||||
"ca": {
|
||||
"doLogIn": "Inicia sessió",
|
||||
"doRegister": "Registra't",
|
||||
@ -4360,5 +4360,5 @@ export const messages= {
|
||||
"invalidParameterMessage": "无效的参数 : {0}",
|
||||
"alreadyLoggedIn": "您已经登录"
|
||||
}
|
||||
} as const;
|
||||
};
|
||||
/* spell-checker: enable */
|
33
src/lib/i18n/kcMessages/login.ts
Normal file
33
src/lib/i18n/kcMessages/login.ts
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
import { kcMessages } from "../generated_kcMessages/login";
|
||||
import { Evt } from "evt";
|
||||
import { objectKeys } from "evt/tools/typeSafety/objectKeys";
|
||||
|
||||
export const evtTermsUpdated = Evt.asNonPostable(Evt.create<void>());
|
||||
|
||||
objectKeys(kcMessages).forEach(kcLanguage =>
|
||||
Object.defineProperty(
|
||||
kcMessages[kcLanguage],
|
||||
"termsText",
|
||||
(() => {
|
||||
|
||||
let value = kcMessages[kcLanguage].termsText;
|
||||
|
||||
return {
|
||||
"enumerable": true,
|
||||
"get": () => value,
|
||||
"set": (newValue: string) => {
|
||||
|
||||
Evt.asPostable(evtTermsUpdated).post();
|
||||
|
||||
value = newValue;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
})()
|
||||
)
|
||||
);
|
||||
|
||||
export { kcMessages };
|
||||
|
@ -1,9 +1,10 @@
|
||||
|
||||
import { createUseGlobalState } from "powerhooks";
|
||||
import { kcContext } from "../kcContext";
|
||||
import { kcContext } from "../KcContext";
|
||||
import { getBestMatchAmongKcLanguageTag } from "./KcLanguageTag";
|
||||
|
||||
export const { useKcLanguageTag } = createUseGlobalState(
|
||||
//export const { useKcLanguageTag, evtKcLanguageTag } = createUseGlobalState(
|
||||
const wrap = createUseGlobalState(
|
||||
"kcLanguageTag",
|
||||
() => getBestMatchAmongKcLanguageTag(
|
||||
kcContext?.locale?.current ??
|
||||
@ -11,3 +12,12 @@ export const { useKcLanguageTag } = createUseGlobalState(
|
||||
),
|
||||
{ "persistance": "cookie" }
|
||||
);
|
||||
|
||||
export const { useKcLanguageTag } = wrap;
|
||||
|
||||
export function getEvtKcLanguage() {
|
||||
return wrap.evtKcLanguageTag;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -1,20 +1,26 @@
|
||||
|
||||
import { useCallback, useReducer } from "react";
|
||||
import { useKcLanguageTag } from "./useKcLanguageTag";
|
||||
import { messages } from "./generated_messages/login";
|
||||
import { useConstCallback } from "powerhooks";
|
||||
import { kcMessages, evtTermsUpdated } from "./kcMessages/login";
|
||||
import type { ReactNode } from "react";
|
||||
import { id } from "evt/tools/typeSafety/id";
|
||||
import { useEvt } from "evt/hooks";
|
||||
//@ts-ignore
|
||||
import * as markdown from "markdown";
|
||||
|
||||
export type MessageKey = keyof typeof messages["en"];
|
||||
export type MessageKey = keyof typeof kcMessages["en"];
|
||||
|
||||
export function useKcMessage() {
|
||||
|
||||
const { kcLanguageTag } = useKcLanguageTag();
|
||||
|
||||
const msgStr = useConstCallback(
|
||||
const [trigger, forceUpdate] = useReducer((counter: number) => counter + 1, 0);
|
||||
|
||||
useEvt(ctx => evtTermsUpdated.attach(ctx, forceUpdate), []);
|
||||
|
||||
const msgStr = useCallback(
|
||||
(key: MessageKey, ...args: (string | undefined)[]): string => {
|
||||
|
||||
let str: string = messages[kcLanguageTag as any as "en"][key] ?? messages["en"][key];
|
||||
let str: string = kcMessages[kcLanguageTag as any as "en"][key] ?? kcMessages["en"][key];
|
||||
|
||||
args.forEach((arg, i) => {
|
||||
|
||||
@ -28,14 +34,21 @@ export function useKcMessage() {
|
||||
|
||||
return str;
|
||||
|
||||
}
|
||||
},
|
||||
[kcLanguageTag, trigger]
|
||||
);
|
||||
|
||||
const msg = useConstCallback(
|
||||
id<(...args: Parameters<typeof msgStr>) => ReactNode>(
|
||||
(key, ...args) =>
|
||||
<span className={key} dangerouslySetInnerHTML={{ "__html": msgStr(key, ...args) }} />
|
||||
)
|
||||
const msg = useCallback<(...args: Parameters<typeof msgStr>) => ReactNode>(
|
||||
(key, ...args) =>
|
||||
<span
|
||||
className={key}
|
||||
dangerouslySetInnerHTML={{
|
||||
"__html":
|
||||
markdown.toHTML(msgStr(key, ...args))
|
||||
}}
|
||||
/>
|
||||
,
|
||||
[kcLanguageTag, trigger]
|
||||
);
|
||||
|
||||
return { msg, msgStr };
|
||||
|
@ -1,8 +1,9 @@
|
||||
export * from "./kcContext";
|
||||
export * from "./KcContext";
|
||||
|
||||
export * from "./i18n/KcLanguageTag";
|
||||
export * from "./i18n/useKcLanguageTag";
|
||||
export * from "./i18n/useKcMessage";
|
||||
export * from "./i18n/kcMessages/login";
|
||||
|
||||
export * from "./components/KcProps";
|
||||
export * from "./components/Login";
|
||||
@ -12,5 +13,9 @@ export * from "./components/Info";
|
||||
export * from "./components/Error";
|
||||
export * from "./components/LoginResetPassword";
|
||||
export * from "./components/LoginVerifyEmail";
|
||||
export * from "./keycloakJsAdapter";
|
||||
|
||||
export * from "./tools/assert";
|
||||
export * from "./tools/assert";
|
||||
|
||||
|
||||
export * as kcContextMocks from "./kcContextMocks";
|
@ -10,12 +10,18 @@ import type { LanguageLabel } from "./i18n/KcLanguageTag";
|
||||
type ExtractAfterStartingWith<Prefix extends string, StrEnum> =
|
||||
StrEnum extends `${Prefix}${infer U}` ? U : never;
|
||||
|
||||
/** Take theses type definition with a grain of salt.
|
||||
* Some values might be undefined on some pages.
|
||||
* (ex: url.loginAction is undefined on error.ftl)
|
||||
*/
|
||||
export type KcContext =
|
||||
KcContext.Login | KcContext.Register | KcContext.Info |
|
||||
KcContext.Error | KcContext.LoginResetPassword | KcContext.LoginVerifyEmail;
|
||||
KcContext.Error | KcContext.LoginResetPassword | KcContext.LoginVerifyEmail |
|
||||
KcContext.Terms;
|
||||
|
||||
export declare namespace KcContext {
|
||||
|
||||
export type Template = {
|
||||
export type Common = {
|
||||
url: {
|
||||
loginAction: string;
|
||||
resourcesPath: string;
|
||||
@ -27,7 +33,7 @@ export declare namespace KcContext {
|
||||
displayName?: string;
|
||||
displayNameHtml?: string;
|
||||
internationalizationEnabled: boolean;
|
||||
registrationEmailAsUsername: boolean; //<---
|
||||
registrationEmailAsUsername: boolean;
|
||||
};
|
||||
/** Undefined if !realm.internationalizationEnabled */
|
||||
locale?: {
|
||||
@ -55,7 +61,7 @@ export declare namespace KcContext {
|
||||
isAppInitiatedAction: boolean;
|
||||
};
|
||||
|
||||
export type Login = Template & {
|
||||
export type Login = Common & {
|
||||
pageId: "login.ftl";
|
||||
url: {
|
||||
loginResetCredentialsUrl: string;
|
||||
@ -88,7 +94,7 @@ export declare namespace KcContext {
|
||||
};
|
||||
};
|
||||
|
||||
export type Register = Template & {
|
||||
export type Register = Common & {
|
||||
pageId: "register.ftl";
|
||||
url: {
|
||||
registrationAction: string;
|
||||
@ -117,11 +123,10 @@ export declare namespace KcContext {
|
||||
};
|
||||
passwordRequired: boolean;
|
||||
recaptchaRequired: boolean;
|
||||
/** undefined if !recaptchaRequired */
|
||||
recaptchaSiteKey?: string;
|
||||
};
|
||||
|
||||
export type Info = Template & {
|
||||
export type Info = Common & {
|
||||
pageId: "info.ftl";
|
||||
messageHeader?: string;
|
||||
requiredActions?: ExtractAfterStartingWith<"requiredAction.", MessageKey>[];
|
||||
@ -133,27 +138,33 @@ export declare namespace KcContext {
|
||||
}
|
||||
};
|
||||
|
||||
export type Error = Template & {
|
||||
export type Error = Common & {
|
||||
pageId: "error.ftl";
|
||||
client?: {
|
||||
baseUrl?: string;
|
||||
}
|
||||
};
|
||||
|
||||
export type LoginResetPassword = Template & {
|
||||
export type LoginResetPassword = Common & {
|
||||
pageId: "login-reset-password.ftl";
|
||||
realm: {
|
||||
loginWithEmailAllowed: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export type LoginVerifyEmail = Template & {
|
||||
export type LoginVerifyEmail = Common & {
|
||||
pageId: "login-verify-email.ftl";
|
||||
};
|
||||
|
||||
export type Terms = Common & {
|
||||
pageId: "terms.ftl";
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
doExtends<KcContext["pageId"], PageId>();
|
||||
doExtends<PageId, KcContext["pageId"]>();
|
||||
|
||||
export const kcContext = id<KcContext | undefined>((window as any)[ftlValuesGlobalName]);
|
||||
|
||||
|
||||
|
206
src/lib/kcContextMocks/index.ts
Normal file
206
src/lib/kcContextMocks/index.ts
Normal file
@ -0,0 +1,206 @@
|
||||
|
||||
|
||||
import type { KcContext } from "../KcContext";
|
||||
import { getEvtKcLanguage } from "../i18n/useKcLanguageTag";
|
||||
import { getKcLanguageTagLabel } from "../i18n/KcLanguageTag";
|
||||
//NOTE: Aside because we want to be able to import them from node
|
||||
import { resourcesCommonPath, resourcesPath } from "./urlResourcesPath";
|
||||
|
||||
const kcCommonContext: KcContext.Common = {
|
||||
"url": {
|
||||
"loginAction": "#",
|
||||
"resourcesPath": `${process.env["PUBLIC_URL"]}/${resourcesPath}`,
|
||||
"resourcesCommonPath": `${process.env["PUBLIC_URL"]}/${resourcesCommonPath}`,
|
||||
"loginRestartFlowUrl": "/auth/realms/myrealm/login-actions/restart?client_id=account&tab_id=HoAx28ja4xg",
|
||||
"loginUrl": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg",
|
||||
},
|
||||
"realm": {
|
||||
"displayName": "myrealm",
|
||||
"displayNameHtml": "myrealm",
|
||||
"internationalizationEnabled": true,
|
||||
"registrationEmailAsUsername": true,
|
||||
},
|
||||
"locale": {
|
||||
"supported": [
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=de",
|
||||
"languageTag": "de"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=no",
|
||||
"languageTag": "no"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ru",
|
||||
"languageTag": "ru"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sv",
|
||||
"languageTag": "sv"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pt-BR",
|
||||
"languageTag": "pt-BR"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=lt",
|
||||
"languageTag": "lt"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=en",
|
||||
"languageTag": "en"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=it",
|
||||
"languageTag": "it"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=fr",
|
||||
"languageTag": "fr"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=zh-CN",
|
||||
"languageTag": "zh-CN"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=es",
|
||||
"languageTag": "es"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=cs",
|
||||
"languageTag": "cs"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ja",
|
||||
"languageTag": "ja"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=sk",
|
||||
"languageTag": "sk"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=pl",
|
||||
"languageTag": "pl"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=ca",
|
||||
"languageTag": "ca"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=nl",
|
||||
"languageTag": "nl"
|
||||
},
|
||||
{
|
||||
"url": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg&execution=ee6c2834-46a4-4a20-a1b6-f6d6f6451b36&kc_locale=tr",
|
||||
"languageTag": "tr"
|
||||
}
|
||||
],
|
||||
"current": null as any
|
||||
},
|
||||
"auth": {
|
||||
"showUsername": false,
|
||||
"showResetCredentials": false,
|
||||
"showTryAnotherWayLink": false
|
||||
},
|
||||
"scripts": [],
|
||||
"message": {
|
||||
"type": "success",
|
||||
"summary": "This is a test message"
|
||||
},
|
||||
"isAppInitiatedAction": false,
|
||||
};
|
||||
|
||||
Object.defineProperty(
|
||||
kcCommonContext.locale!,
|
||||
"current",
|
||||
{
|
||||
"get": () => getKcLanguageTagLabel(getEvtKcLanguage().state),
|
||||
"enumerable": true
|
||||
}
|
||||
);
|
||||
|
||||
export const kcLoginContext: KcContext.Login = {
|
||||
...kcCommonContext,
|
||||
"pageId": "login.ftl",
|
||||
"url": {
|
||||
...kcCommonContext.url,
|
||||
"loginResetCredentialsUrl": "/auth/realms/myrealm/login-actions/reset-credentials?client_id=account&tab_id=HoAx28ja4xg",
|
||||
"registrationUrl": "/auth/realms/myrealm/login-actions/registration?client_id=account&tab_id=HoAx28ja4xg"
|
||||
},
|
||||
"realm": {
|
||||
...kcCommonContext.realm,
|
||||
"loginWithEmailAllowed": true,
|
||||
"rememberMe": true,
|
||||
"password": true,
|
||||
"resetPasswordAllowed": true,
|
||||
"registrationAllowed": true
|
||||
},
|
||||
"auth": kcCommonContext.auth!,
|
||||
"social": {
|
||||
"displayInfo": true
|
||||
},
|
||||
"usernameEditDisabled": false,
|
||||
"login": {
|
||||
"rememberMe": false
|
||||
},
|
||||
"registrationDisabled": false,
|
||||
};
|
||||
|
||||
export const kcRegisterContext: KcContext.Register = {
|
||||
...kcCommonContext,
|
||||
"url": {
|
||||
...kcLoginContext.url,
|
||||
"registrationAction": "http://localhost:8080/auth/realms/myrealm/login-actions/registration?session_code=gwZdUeO7pbYpFTRxiIxRg_QtzMbtFTKrNu6XW_f8asM&execution=12146ce0-b139-4bbd-b25b-0eccfee6577e&client_id=account&tab_id=uS8lYfebLa0"
|
||||
},
|
||||
"messagesPerField": {
|
||||
"printIfExists": (...[,x]) => x
|
||||
},
|
||||
"scripts": [],
|
||||
"isAppInitiatedAction": false,
|
||||
"pageId": "register.ftl",
|
||||
"register": {
|
||||
"formData": {}
|
||||
},
|
||||
"passwordRequired": true,
|
||||
"recaptchaRequired": false
|
||||
};
|
||||
|
||||
export const kcInfoContext: KcContext.Info ={
|
||||
...kcCommonContext,
|
||||
"pageId": "info.ftl",
|
||||
"messageHeader": "<Message header>",
|
||||
"requiredActions": undefined,
|
||||
"skipLink": false,
|
||||
"actionUri": "#",
|
||||
"client": {
|
||||
"baseUrl": "#"
|
||||
}
|
||||
};
|
||||
|
||||
export const kcErrorContext: KcContext.Error = {
|
||||
...kcCommonContext,
|
||||
"pageId": "error.ftl",
|
||||
"client": {
|
||||
"baseUrl": "#"
|
||||
}
|
||||
};
|
||||
|
||||
export const kcLoginResetPasswordContext: KcContext.LoginResetPassword = {
|
||||
...kcCommonContext,
|
||||
"pageId": "login-reset-password.ftl",
|
||||
"realm":{
|
||||
...kcCommonContext.realm,
|
||||
"loginWithEmailAllowed": false
|
||||
}
|
||||
};
|
||||
|
||||
export const kcLoginVerifyEmailContext: KcContext.LoginVerifyEmail = {
|
||||
...kcCommonContext,
|
||||
"pageId": "login-verify-email.ftl"
|
||||
};
|
||||
|
||||
export const kcTermContext: KcContext.Terms = {
|
||||
...kcCommonContext,
|
||||
"pageId": "terms.ftl"
|
||||
};
|
||||
|
6
src/lib/kcContextMocks/urlResourcesPath.ts
Normal file
6
src/lib/kcContextMocks/urlResourcesPath.ts
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
import { join as pathJoin } from "path";
|
||||
|
||||
export const subDirOfPublicDirBasename = "keycloak_static";
|
||||
export const resourcesPath = pathJoin(subDirOfPublicDirBasename, "/resources");
|
||||
export const resourcesCommonPath = pathJoin(subDirOfPublicDirBasename, "/resources_common");
|
118
src/lib/keycloakJsAdapter.ts
Normal file
118
src/lib/keycloakJsAdapter.ts
Normal file
@ -0,0 +1,118 @@
|
||||
|
||||
|
||||
export declare namespace keycloak_js {
|
||||
|
||||
export type KeycloakPromiseCallback<T> = (result: T) => void;
|
||||
export class KeycloakPromise<TSuccess, TError> extends Promise<TSuccess> {
|
||||
success(callback: KeycloakPromiseCallback<TSuccess>): KeycloakPromise<TSuccess, TError>;
|
||||
error(callback: KeycloakPromiseCallback<TError>): KeycloakPromise<TSuccess, TError>;
|
||||
}
|
||||
export interface KeycloakAdapter {
|
||||
login(options?: KeycloakLoginOptions): KeycloakPromise<void, void>;
|
||||
logout(options?: KeycloakLogoutOptions): KeycloakPromise<void, void>;
|
||||
register(options?: KeycloakLoginOptions): KeycloakPromise<void, void>;
|
||||
accountManagement(): KeycloakPromise<void, void>;
|
||||
redirectUri(options: { redirectUri: string; }, encodeHash: boolean): string;
|
||||
}
|
||||
export interface KeycloakLogoutOptions {
|
||||
redirectUri?: string;
|
||||
}
|
||||
export interface KeycloakLoginOptions {
|
||||
scope?: string;
|
||||
redirectUri?: string;
|
||||
prompt?: 'none' | 'login';
|
||||
action?: string;
|
||||
maxAge?: number;
|
||||
loginHint?: string;
|
||||
idpHint?: string;
|
||||
locale?: string;
|
||||
cordovaOptions?: { [optionName: string]: string };
|
||||
}
|
||||
|
||||
export type KeycloakInstance = Record<
|
||||
"createLoginUrl" |
|
||||
"createLogoutUrl" |
|
||||
"createRegisterUrl",
|
||||
(options: KeycloakLoginOptions | undefined) => string
|
||||
> & {
|
||||
createAccountUrl(): string;
|
||||
redirectUri?: string;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: This is just a slightly modified version of the default adapter in keycloak-js
|
||||
* The goal here is just to be able to inject search param in url before keycloak redirect.
|
||||
* Our use case for it is to pass over the login screen the states of useGlobalState
|
||||
* namely isDarkModeEnabled, lgn...
|
||||
*/
|
||||
export function createKeycloakAdapter(
|
||||
params: {
|
||||
keycloakInstance: keycloak_js.KeycloakInstance;
|
||||
transformUrlBeforeRedirect(url: string): string;
|
||||
}
|
||||
): keycloak_js.KeycloakAdapter {
|
||||
|
||||
const { keycloakInstance, transformUrlBeforeRedirect } = params;
|
||||
|
||||
const neverResolvingPromise: keycloak_js.KeycloakPromise<void, void> = Object.defineProperties(
|
||||
new Promise(() => { }),
|
||||
{
|
||||
"success": { "value": () => { } },
|
||||
"error": { "value": () => { } }
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
"login": options => {
|
||||
window.location.href=
|
||||
transformUrlBeforeRedirect(
|
||||
keycloakInstance.createLoginUrl(
|
||||
options
|
||||
)
|
||||
);
|
||||
return neverResolvingPromise;
|
||||
},
|
||||
"logout": options => {
|
||||
window.location.replace(
|
||||
transformUrlBeforeRedirect(
|
||||
keycloakInstance.createLogoutUrl(
|
||||
options
|
||||
)
|
||||
)
|
||||
);
|
||||
return neverResolvingPromise;
|
||||
},
|
||||
"register": options => {
|
||||
window.location.href =
|
||||
transformUrlBeforeRedirect(
|
||||
keycloakInstance.createRegisterUrl(
|
||||
options
|
||||
)
|
||||
);
|
||||
|
||||
return neverResolvingPromise;
|
||||
},
|
||||
"accountManagement": () => {
|
||||
var accountUrl = transformUrlBeforeRedirect(keycloakInstance.createAccountUrl());
|
||||
if (typeof accountUrl !== 'undefined') {
|
||||
window.location.href = accountUrl;
|
||||
} else {
|
||||
throw new Error("Not supported by the OIDC server");
|
||||
}
|
||||
return neverResolvingPromise;
|
||||
},
|
||||
"redirectUri": options => {
|
||||
if (options && options.redirectUri) {
|
||||
return options.redirectUri;
|
||||
} else if (keycloakInstance.redirectUri) {
|
||||
return keycloakInstance.redirectUri;
|
||||
} else {
|
||||
return window.location.href;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
|
||||
import { join as pathJoin } from "path";
|
||||
import { generateKeycloakThemeResources } from "../bin/build-keycloak-theme/generateKeycloakThemeResources";
|
||||
import {
|
||||
import {
|
||||
setupSampleReactProject,
|
||||
sampleReactProjectDirPath
|
||||
} from "./setupSampleReactProject";
|
||||
@ -9,8 +9,9 @@ import {
|
||||
setupSampleReactProject();
|
||||
|
||||
generateKeycloakThemeResources({
|
||||
"themeName": "onyxia-ui",
|
||||
"themeName": "keycloakify-demo-app",
|
||||
"reactAppBuildDirPath": pathJoin(sampleReactProjectDirPath, "build"),
|
||||
"keycloakThemeBuildingDirPath": pathJoin(sampleReactProjectDirPath, "build_keycloak_theme")
|
||||
"keycloakThemeBuildingDirPath": pathJoin(sampleReactProjectDirPath, "build_keycloak_theme"),
|
||||
"urlPathname": "/keycloakify-demo-app/"
|
||||
});
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
|
||||
import {
|
||||
replaceImportFromStaticInJsCode,
|
||||
replaceImportFromStaticInCssCode,
|
||||
replaceImportsFromStaticInJsCode,
|
||||
replaceImportsInCssCode,
|
||||
generateCssCodeToDefineGlobals
|
||||
} from "../bin/build-keycloak-theme/replaceImportFromStatic";
|
||||
|
||||
const { fixedJsCode } = replaceImportFromStaticInJsCode({
|
||||
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
|
||||
"ftlValuesGlobalName": "keycloakFtlValues",
|
||||
"jsCode": `
|
||||
function f() {
|
||||
@ -24,7 +24,7 @@ const { fixedJsCode } = replaceImportFromStaticInJsCode({
|
||||
|
||||
console.log({ fixedJsCode });
|
||||
|
||||
const { fixedCssCode, cssGlobalsToDefine } = replaceImportFromStaticInCssCode({
|
||||
const { fixedCssCode, cssGlobalsToDefine } = replaceImportsInCssCode({
|
||||
"cssCode": `
|
||||
|
||||
.my-div {
|
||||
@ -45,6 +45,6 @@ const { fixedCssCode, cssGlobalsToDefine } = replaceImportFromStaticInCssCode({
|
||||
console.log({ fixedCssCode, cssGlobalsToDefine });
|
||||
|
||||
|
||||
const { cssCodeToPrependInHead } = generateCssCodeToDefineGlobals({ cssGlobalsToDefine });
|
||||
const { cssCodeToPrependInHead } = generateCssCodeToDefineGlobals({ cssGlobalsToDefine, "urlPathname": "/" });
|
||||
|
||||
console.log({ cssCodeToPrependInHead });
|
343
yarn.lock
343
yarn.lock
@ -27,9 +27,9 @@
|
||||
integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==
|
||||
|
||||
"@babel/highlight@^7.12.13":
|
||||
version "7.13.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.8.tgz#10b2dac78526424dfc1f47650d0e415dfd9dc481"
|
||||
integrity sha512-4vrIhfJyfNf+lCtXC2ck1rKSzDwciqF7IWFhXXrSOUC2O5DrVp+w4c6ed4AllTxhTkUP5x2tYj41VaxdVMMRDw==
|
||||
version "7.13.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.10.tgz#a8b2a66148f5b27d666b15d81774347a731d52d1"
|
||||
integrity sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.12.11"
|
||||
chalk "^2.0.0"
|
||||
@ -43,9 +43,9 @@
|
||||
"@babel/helper-plugin-utils" "^7.12.13"
|
||||
|
||||
"@babel/runtime@^7.7.2":
|
||||
version "7.13.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.9.tgz#97dbe2116e2630c489f22e0656decd60aaa1fcee"
|
||||
integrity sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==
|
||||
version "7.13.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d"
|
||||
integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
@ -109,9 +109,9 @@
|
||||
integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==
|
||||
|
||||
"@emotion/serialize@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.0.tgz#1a61f4f037cf39995c97fc80ebe99abc7b191ca9"
|
||||
integrity sha512-zt1gm4rhdo5Sry8QpCOpopIUIKU+mUSpV9WNmFILUraatm5dttNEaYzUWWSboSMUE6PtN2j1cAsuvcugfdI3mw==
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.1.tgz#322cdebfdbb5a88946f17006548191859b9b0855"
|
||||
integrity sha512-TXlKs5sgUKhFlszp/rg4lIAZd7UUSmJpwaf9/lAEFcUh2vPi32i7x4wk7O8TN8L8v2Ol8k0CxnhRBY0zQalTxA==
|
||||
dependencies:
|
||||
"@emotion/hash" "^0.8.0"
|
||||
"@emotion/memoize" "^0.7.4"
|
||||
@ -139,117 +139,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
|
||||
integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
|
||||
|
||||
"@octokit/auth-token@^2.4.4":
|
||||
version "2.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.4.5.tgz#568ccfb8cb46f36441fac094ce34f7a875b197f3"
|
||||
integrity sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==
|
||||
dependencies:
|
||||
"@octokit/types" "^6.0.3"
|
||||
|
||||
"@octokit/core@^3.2.3":
|
||||
version "3.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.2.5.tgz#57becbd5fd789b0592b915840855f3a5f233d554"
|
||||
integrity sha512-+DCtPykGnvXKWWQI0E1XD+CCeWSBhB6kwItXqfFmNBlIlhczuDPbg+P6BtLnVBaRJDAjv+1mrUJuRsFSjktopg==
|
||||
dependencies:
|
||||
"@octokit/auth-token" "^2.4.4"
|
||||
"@octokit/graphql" "^4.5.8"
|
||||
"@octokit/request" "^5.4.12"
|
||||
"@octokit/types" "^6.0.3"
|
||||
before-after-hook "^2.1.0"
|
||||
universal-user-agent "^6.0.0"
|
||||
|
||||
"@octokit/endpoint@^6.0.1":
|
||||
version "6.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.11.tgz#082adc2aebca6dcefa1fb383f5efb3ed081949d1"
|
||||
integrity sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ==
|
||||
dependencies:
|
||||
"@octokit/types" "^6.0.3"
|
||||
is-plain-object "^5.0.0"
|
||||
universal-user-agent "^6.0.0"
|
||||
|
||||
"@octokit/graphql@^4.5.8":
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.6.0.tgz#f9abca55f82183964a33439d5264674c701c3327"
|
||||
integrity sha512-CJ6n7izLFXLvPZaWzCQDjU/RP+vHiZmWdOunaCS87v+2jxMsW9FB5ktfIxybRBxZjxuJGRnxk7xJecWTVxFUYQ==
|
||||
dependencies:
|
||||
"@octokit/request" "^5.3.0"
|
||||
"@octokit/types" "^6.0.3"
|
||||
universal-user-agent "^6.0.0"
|
||||
|
||||
"@octokit/openapi-types@^5.3.0":
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-5.3.1.tgz#a49d119a1b9e47b7a9f5133ab14be2d8afaa183d"
|
||||
integrity sha512-TvVk2QuIA0lQZcIMd6xbdGaGDVeNYIOa3l1ZVagAIk5K3t/WMYbcg4BISNDhzdVhm/TgQB26frAgd/GV81aHJA==
|
||||
|
||||
"@octokit/plugin-paginate-rest@^2.6.2":
|
||||
version "2.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.11.0.tgz#3568c43896a3355f4a0bbb3a64f443b2abdc760d"
|
||||
integrity sha512-7L9xQank2G3r1dGqrVPo1z62V5utbykOUzlmNHPz87Pww/JpZQ9KyG5CHtUzgmB4n5iDRKYNK/86A8D98HP0yA==
|
||||
dependencies:
|
||||
"@octokit/types" "^6.11.0"
|
||||
|
||||
"@octokit/plugin-request-log@^1.0.2":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz#70a62be213e1edc04bb8897ee48c311482f9700d"
|
||||
integrity sha512-4RFU4li238jMJAzLgAwkBAw+4Loile5haQMQr+uhFq27BmyJXcXSKvoQKqh0agsZEiUlW6iSv3FAgvmGkur7OQ==
|
||||
|
||||
"@octokit/plugin-rest-endpoint-methods@4.13.4":
|
||||
version "4.13.4"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.13.4.tgz#093b130d558760f6bc912c6f622ce502522410af"
|
||||
integrity sha512-MGxptzVfiP8O+aydC/riheYzS/yJ9P16M29OuvtZep/sF5sKuOCQP8Wf83YCKXRsQF+ZpYfke2snbPPSIMZKzg==
|
||||
dependencies:
|
||||
"@octokit/types" "^6.12.0"
|
||||
deprecation "^2.3.1"
|
||||
|
||||
"@octokit/request-error@^2.0.0":
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.0.5.tgz#72cc91edc870281ad583a42619256b380c600143"
|
||||
integrity sha512-T/2wcCFyM7SkXzNoyVNWjyVlUwBvW3igM3Btr/eKYiPmucXTtkxt2RBsf6gn3LTzaLSLTQtNmvg+dGsOxQrjZg==
|
||||
dependencies:
|
||||
"@octokit/types" "^6.0.3"
|
||||
deprecation "^2.0.0"
|
||||
once "^1.4.0"
|
||||
|
||||
"@octokit/request@^5.3.0", "@octokit/request@^5.4.12":
|
||||
version "5.4.14"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.14.tgz#ec5f96f78333bb2af390afa5ff66f114b063bc96"
|
||||
integrity sha512-VkmtacOIQp9daSnBmDI92xNIeLuSRDOIuplp/CJomkvzt7M18NXgG044Cx/LFKLgjKt9T2tZR6AtJayba9GTSA==
|
||||
dependencies:
|
||||
"@octokit/endpoint" "^6.0.1"
|
||||
"@octokit/request-error" "^2.0.0"
|
||||
"@octokit/types" "^6.7.1"
|
||||
deprecation "^2.0.0"
|
||||
is-plain-object "^5.0.0"
|
||||
node-fetch "^2.6.1"
|
||||
once "^1.4.0"
|
||||
universal-user-agent "^6.0.0"
|
||||
|
||||
"@octokit/rest@^18.0.0":
|
||||
version "18.3.4"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.3.4.tgz#8e7ab02cd509e2fe7e71917a54254a8f8b827f20"
|
||||
integrity sha512-NES0pHbwyFB1D0jrLkdnIXgEmze/gLE0JoSNgfAe4vwD77/qaQGO/lRWNuPPsoBVBjiW6mmA9CU5cYHujJTKQA==
|
||||
dependencies:
|
||||
"@octokit/core" "^3.2.3"
|
||||
"@octokit/plugin-paginate-rest" "^2.6.2"
|
||||
"@octokit/plugin-request-log" "^1.0.2"
|
||||
"@octokit/plugin-rest-endpoint-methods" "4.13.4"
|
||||
|
||||
"@octokit/types@^6.0.3", "@octokit/types@^6.11.0", "@octokit/types@^6.12.0", "@octokit/types@^6.7.1":
|
||||
version "6.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.12.0.tgz#8376fd60edfd5d1eebfeedb994c6bcb5b862c7a1"
|
||||
integrity sha512-KwOf16soD7aDEEi/PgNeJlHzjZPfrmmNy+7WezSdrpnqZ7YImBJcNnX9+5RUHt1MnA4h8oISRHTqaZDGsX9DRQ==
|
||||
dependencies:
|
||||
"@octokit/openapi-types" "^5.3.0"
|
||||
|
||||
"@types/comment-json@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/comment-json/-/comment-json-1.1.1.tgz#b4ae889912a93e64619f97989aecaff8ce889dca"
|
||||
integrity sha512-U70oEqvnkeSSp8BIJwJclERtT13rd9ejK7XkIzMCQQePZe3VW1b7iQggXyW4ZvfGtGeXD0pZw24q5iWNe++HqQ==
|
||||
|
||||
"@types/node@^10.0.0":
|
||||
version "10.17.54"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.54.tgz#a737488631aca3ec7bd9f6229d77f1079e444793"
|
||||
integrity sha512-c8Lm7+hXdSPmWH4B9z/P/xIXhFK3mCQin4yCYMd2p1qpMG5AfgyJuYZ+3q2dT7qLiMMMGMd5dnkFpdqJARlvtQ==
|
||||
version "10.17.55"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.55.tgz#a147f282edec679b894d4694edb5abeb595fecbd"
|
||||
integrity sha512-koZJ89uLZufDvToeWO5BrC4CR4OUfHnUz2qoPs/daQH6qq3IN62QFxCTZ+bKaCE0xaoCAJYE4AXre8AbghCrhg==
|
||||
|
||||
"@types/parse-json@^4.0.0":
|
||||
version "4.0.0"
|
||||
@ -262,13 +155,19 @@
|
||||
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
|
||||
|
||||
"@types/react@^17.0.0":
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.2.tgz#3de24c4efef902dd9795a49c75f760cbe4f7a5a8"
|
||||
integrity sha512-Xt40xQsrkdvjn1EyWe1Bc0dJLcil/9x2vAuW7ya+PuQip4UYUaXyhzWmAbwRsdMgwOFHpfp7/FFZebDU6Y8VHA==
|
||||
version "17.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.3.tgz#ba6e215368501ac3826951eef2904574c262cc79"
|
||||
integrity sha512-wYOUxIgs2HZZ0ACNiIayItyluADNbONl7kt8lkLjVK8IitMH5QMyAh75Fwhmo37r1m7L2JaFj03sIfxBVDvRAg==
|
||||
dependencies:
|
||||
"@types/prop-types" "*"
|
||||
"@types/scheduler" "*"
|
||||
csstype "^3.0.2"
|
||||
|
||||
"@types/scheduler@*":
|
||||
version "0.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275"
|
||||
integrity sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==
|
||||
|
||||
ansi-regex@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
|
||||
@ -302,11 +201,6 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
before-after-hook@^2.1.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.0.tgz#09c40d92e936c64777aa385c4e9b904f8147eaf0"
|
||||
integrity sha512-jH6rKQIfroBbhEXVmI7XmXe3ix5S/PgJqpzdDPnR8JGLHWNYLsYZ6tK5iWOF/Ra3oqEX0NobXGlzbiylIzVphQ==
|
||||
|
||||
boolbase@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||
@ -391,21 +285,6 @@ color-name@~1.1.4:
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
|
||||
commander@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
|
||||
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
|
||||
|
||||
comment-json@^3.0.2:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/comment-json/-/comment-json-3.0.3.tgz#0cadacd6278602b57b8c51b1814dc5d311d228c4"
|
||||
integrity sha512-P7XwYkC3qjIK45EAa9c5Y3lR7SMXhJqwFdWg3niAIAcbk3zlpKDdajV8Hyz/Y3sGNn3l+YNMl8A2N/OubSArHg==
|
||||
dependencies:
|
||||
core-util-is "^1.0.2"
|
||||
esprima "^4.0.1"
|
||||
has-own-prop "^2.0.0"
|
||||
repeat-string "^1.6.1"
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
@ -431,7 +310,7 @@ copyfiles@^2.4.1:
|
||||
untildify "^4.0.0"
|
||||
yargs "^16.1.0"
|
||||
|
||||
core-util-is@^1.0.2, core-util-is@~1.0.0:
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
|
||||
@ -476,29 +355,6 @@ d@1, d@^1.0.1:
|
||||
es5-ext "^0.10.50"
|
||||
type "^1.0.1"
|
||||
|
||||
denoify@^0.6.5:
|
||||
version "0.6.5"
|
||||
resolved "https://registry.yarnpkg.com/denoify/-/denoify-0.6.5.tgz#06512be38026ec0f2033db9566d07428e63d56bf"
|
||||
integrity sha512-AhKBlvO91QdP5T/fRcxiMj3b6cDe5ucr+7YVlePXCuV/kImbiR0V1qjGTCfmVuC3wvRn5Xf6r34qJJRbo86AqA==
|
||||
dependencies:
|
||||
"@octokit/rest" "^18.0.0"
|
||||
"@types/comment-json" "^1.1.1"
|
||||
commander "^4.1.1"
|
||||
comment-json "^3.0.2"
|
||||
evt "^1.9.2"
|
||||
get-github-default-branch-name "^0.0.4"
|
||||
gitignore-parser "0.0.2"
|
||||
glob "^7.1.6"
|
||||
node-fetch "^2.6.0"
|
||||
path-depth "^1.0.0"
|
||||
scripting-tools "^0.19.13"
|
||||
url-join "^4.0.1"
|
||||
|
||||
deprecation@^2.0.0, deprecation@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919"
|
||||
integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==
|
||||
|
||||
dom-serializer@^1.0.1, dom-serializer@~1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.2.0.tgz#3433d9136aeb3c627981daa385fc7f32d27c48f1"
|
||||
@ -521,9 +377,9 @@ domhandler@^4.0.0:
|
||||
domelementtype "^2.1.0"
|
||||
|
||||
domutils@^2.4.3, domutils@^2.4.4:
|
||||
version "2.4.4"
|
||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.4.4.tgz#282739c4b150d022d34699797369aad8d19bbbd3"
|
||||
integrity sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA==
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.5.0.tgz#42f49cffdabb92ad243278b331fd761c1c2d3039"
|
||||
integrity sha512-Ho16rzNMOFk2fPwChGh3D2D9OEHAfG19HgmRR2l+WLSsIstNsAYBzePH412bL0y5T44ejABIVfTHQ8nqi/tBCg==
|
||||
dependencies:
|
||||
dom-serializer "^1.0.1"
|
||||
domelementtype "^2.0.1"
|
||||
@ -602,11 +458,6 @@ escape-string-regexp@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
|
||||
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
|
||||
|
||||
esprima@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
|
||||
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
|
||||
|
||||
event-emitter@^0.3.5:
|
||||
version "0.3.5"
|
||||
resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39"
|
||||
@ -615,14 +466,6 @@ event-emitter@^0.3.5:
|
||||
d "1"
|
||||
es5-ext "~0.10.14"
|
||||
|
||||
evt@2.0.0-beta.13:
|
||||
version "2.0.0-beta.13"
|
||||
resolved "https://registry.yarnpkg.com/evt/-/evt-2.0.0-beta.13.tgz#a84828cc15b535fabdeaf551982d0705ed428a6f"
|
||||
integrity sha512-Y71l8BU1r/hs7MOyaNb5PTf8Jp+5h8Vt6T8ixvJSqDmKhwCjjCVKP2b1E3UE+Qlr1bZnCOWnVTucTeTeJghWEQ==
|
||||
dependencies:
|
||||
minimal-polyfills "^2.1.5"
|
||||
run-exclusive "^2.2.14"
|
||||
|
||||
evt@2.0.0-beta.15:
|
||||
version "2.0.0-beta.15"
|
||||
resolved "https://registry.yarnpkg.com/evt/-/evt-2.0.0-beta.15.tgz#a34ef224c827152c06f29157a9af5a41644e1a36"
|
||||
@ -631,14 +474,6 @@ evt@2.0.0-beta.15:
|
||||
minimal-polyfills "^2.1.5"
|
||||
run-exclusive "^2.2.14"
|
||||
|
||||
evt@^1.9.2:
|
||||
version "1.9.12"
|
||||
resolved "https://registry.yarnpkg.com/evt/-/evt-1.9.12.tgz#8d06177259cbcb09ef936e18945bf7ddd087170c"
|
||||
integrity sha512-u8wC4Xif2pcDJ9cEm0wzWCIQb+Y214m1eUgsgm2hVIuXuvC6LToryA0Ecl1O8Slii2E9l6USLsyxXWntjlnIbw==
|
||||
dependencies:
|
||||
minimal-polyfills "^2.1.5"
|
||||
run-exclusive "^2.2.14"
|
||||
|
||||
ext@^1.1.2:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244"
|
||||
@ -666,20 +501,7 @@ get-caller-file@^2.0.5:
|
||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||
|
||||
get-github-default-branch-name@^0.0.4:
|
||||
version "0.0.4"
|
||||
resolved "https://registry.yarnpkg.com/get-github-default-branch-name/-/get-github-default-branch-name-0.0.4.tgz#9c0c6606ba606edb136d2fd26e4d515b69f2de90"
|
||||
integrity sha512-ltOGC9Jk0k8boe48Gk7SkJErwxt7MhwXtbNrBUyNCZcwcXSmGRdkKb2u0YO250PGvPsUtdqRjg7lVuIk1VtpCg==
|
||||
dependencies:
|
||||
"@octokit/rest" "^18.0.0"
|
||||
scripting-tools "^0.19.12"
|
||||
|
||||
gitignore-parser@0.0.2:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/gitignore-parser/-/gitignore-parser-0.0.2.tgz#f61259b985dd91414b9a7168faef9171c2eec5df"
|
||||
integrity sha1-9hJZuYXdkUFLmnFo+u+RccLuxd8=
|
||||
|
||||
glob@^7.0.5, glob@^7.1.3, glob@^7.1.6:
|
||||
glob@^7.0.5, glob@^7.1.3:
|
||||
version "7.1.6"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
|
||||
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
||||
@ -696,11 +518,6 @@ has-flag@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
||||
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
|
||||
|
||||
has-own-prop@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-own-prop/-/has-own-prop-2.0.0.tgz#f0f95d58f65804f5d218db32563bb85b8e0417af"
|
||||
integrity sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==
|
||||
|
||||
has@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
|
||||
@ -708,15 +525,10 @@ has@^1.0.3:
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
|
||||
html-escaper@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-3.0.3.tgz#4d336674652beb1dcbc29ef6b6ba7f6be6fdfed6"
|
||||
integrity sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==
|
||||
|
||||
htmlparser2@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.0.0.tgz#c2da005030390908ca4c91e5629e418e0665ac01"
|
||||
integrity sha512-numTQtDZMoh78zJpaNdJ9MXb2cv5G3jwUoe3dMQODubZvLoGvTE/Ofp6sHvH8OGKcN/8A47pGLi/k58xHP/Tfw==
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.0.1.tgz#422521231ef6d42e56bd411da8ba40aa36e91446"
|
||||
integrity sha512-GDKPd+vk4jvSuvCbyuzx/unmXkk090Azec7LovXP8as1Hn8q9p3hbjmDGbUqqhknw0ajwit6LiiWqfiTUPMK7w==
|
||||
dependencies:
|
||||
domelementtype "^2.0.1"
|
||||
domhandler "^4.0.0"
|
||||
@ -744,6 +556,11 @@ inherits@2, inherits@^2.0.1, inherits@~2.0.1, inherits@~2.0.3:
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
inherits@2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
||||
|
||||
is-arrayish@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
|
||||
@ -761,11 +578,6 @@ is-fullwidth-code-point@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
|
||||
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
|
||||
|
||||
is-plain-object@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
|
||||
integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
|
||||
|
||||
is-promise@^2.2.2:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1"
|
||||
@ -856,11 +668,6 @@ next-tick@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
|
||||
integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
|
||||
|
||||
node-fetch@^2.6.0, node-fetch@^2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||
|
||||
noms@0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/noms/-/noms-0.0.0.tgz#da8ebd9f3af9d6760919b27d9cdc8092a7332859"
|
||||
@ -881,7 +688,7 @@ object-assign@^4.1.1:
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||
|
||||
once@^1.3.0, once@^1.4.0:
|
||||
once@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||
@ -917,11 +724,6 @@ parse5@^6.0.0, parse5@^6.0.1:
|
||||
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
|
||||
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
|
||||
|
||||
path-depth@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-depth/-/path-depth-1.0.0.tgz#88cf881097e171b8b54d450d2167ea76063d3086"
|
||||
integrity sha512-dEiwdXAQyLvOi6ktLqhFhjVelJiVsdp2xBX3BaUtYCCkMRZTwUiq7cha+A0myvAVXRHbXfjhfTf4mNoAWzm2iA==
|
||||
|
||||
path-is-absolute@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
@ -937,12 +739,20 @@ path-type@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
||||
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
||||
|
||||
powerhooks@^0.0.19:
|
||||
version "0.0.19"
|
||||
resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.0.19.tgz#86f4157dbde32cd44082c756ab747c64f6045449"
|
||||
integrity sha512-yaODFWkflrZCSz4lvRQ2O4AjolheiE6oXa1F4mny2LUOwai4ip+zer16fgXEM53R+IiDnqj6ff8wooU5x4GslQ==
|
||||
path@^0.12.7:
|
||||
version "0.12.7"
|
||||
resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f"
|
||||
integrity sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=
|
||||
dependencies:
|
||||
evt "2.0.0-beta.13"
|
||||
process "^0.11.1"
|
||||
util "^0.10.3"
|
||||
|
||||
powerhooks@^0.0.36:
|
||||
version "0.0.36"
|
||||
resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.0.36.tgz#d973d339ad8ca7ce52ea9d288ebe8e138df7d769"
|
||||
integrity sha512-0fEGKLfJmuFeEYDGsAlTuvGKKdqOH059xezosmYRoGurfC1bbUlaNJD+TuzOT5cTGURi88DCLcDnhk/2eLyCyA==
|
||||
dependencies:
|
||||
evt "2.0.0-beta.15"
|
||||
memoizee "^0.4.15"
|
||||
resize-observer-polyfill "^1.5.1"
|
||||
|
||||
@ -951,6 +761,11 @@ process-nextick-args@~2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
||||
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
|
||||
|
||||
process@^0.11.1:
|
||||
version "0.11.10"
|
||||
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
|
||||
integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
|
||||
|
||||
properties-parser@^0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/properties-parser/-/properties-parser-0.3.1.tgz#1316e9539ffbfd93845e369b211022abd478771a"
|
||||
@ -994,11 +809,6 @@ regenerator-runtime@^0.13.4:
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
|
||||
integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
|
||||
|
||||
repeat-string@^1.6.1:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
|
||||
integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
|
||||
|
||||
require-directory@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
|
||||
@ -1041,7 +851,7 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
scripting-tools@^0.19.12, scripting-tools@^0.19.13:
|
||||
scripting-tools@^0.19.13:
|
||||
version "0.19.13"
|
||||
resolved "https://registry.yarnpkg.com/scripting-tools/-/scripting-tools-0.19.13.tgz#836df7d9c2ec99aea91984d1d5b2bd110670afec"
|
||||
integrity sha512-d09H8vzSVa8p4XUTJqHZDbjKDyl5TG3SyPfNPUUkfyOwjwykStmfK8AXyWq7VRWjcgzTpkTiJ9uMk1NytMQY7w==
|
||||
@ -1117,10 +927,10 @@ to-fast-properties@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
|
||||
integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
|
||||
|
||||
tss-react@^0.0.11:
|
||||
version "0.0.11"
|
||||
resolved "https://registry.yarnpkg.com/tss-react/-/tss-react-0.0.11.tgz#1cd061927744cd4fc9b7346e2fd1cfcf896a18d5"
|
||||
integrity sha512-j8CDpHHIl6S6/mX+AmK08v7waPqwgNA7urHOD3qknCgbY79LRlS7he5DF4NUNE/5B4/Btc3F25w+KqgChNbyGw==
|
||||
tss-react@^0.0.12:
|
||||
version "0.0.12"
|
||||
resolved "https://registry.yarnpkg.com/tss-react/-/tss-react-0.0.12.tgz#6463617ae5e7f670742e48e497d8825d59e2a2e9"
|
||||
integrity sha512-oHekukqdaE71uhHx4XEdHy6aMnDYhoHLWB94iy2Fy9X8btH2lJH1joPj0zS1q7+1Xy2TydkLEZsTq3ElVd7ZqA==
|
||||
dependencies:
|
||||
"@emotion/css" "^11.1.3"
|
||||
|
||||
@ -1130,35 +940,32 @@ type@^1.0.1:
|
||||
integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==
|
||||
|
||||
type@^2.0.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/type/-/type-2.3.0.tgz#ada7c045f07ead08abf9e2edd29be1a0c0661132"
|
||||
integrity sha512-rgPIqOdfK/4J9FhiVrZ3cveAjRRo5rsQBAIhnylX874y1DX/kEKSVdLsnuHB6l1KTjHyU01VjiMBHgU2adejyg==
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d"
|
||||
integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==
|
||||
|
||||
typescript@^4.1.5:
|
||||
typescript@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3"
|
||||
integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==
|
||||
|
||||
universal-user-agent@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee"
|
||||
integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==
|
||||
|
||||
untildify@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"
|
||||
integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
|
||||
|
||||
url-join@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7"
|
||||
integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==
|
||||
|
||||
util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
util@^0.10.3:
|
||||
version "0.10.4"
|
||||
resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901"
|
||||
integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==
|
||||
dependencies:
|
||||
inherits "2.0.3"
|
||||
|
||||
wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
@ -1184,14 +991,14 @@ y18n@^5.0.5:
|
||||
integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==
|
||||
|
||||
yaml@^1.7.2:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e"
|
||||
integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==
|
||||
version "1.10.2"
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
|
||||
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
|
||||
|
||||
yargs-parser@^20.2.2:
|
||||
version "20.2.6"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.6.tgz#69f920addf61aafc0b8b89002f5d66e28f2d8b20"
|
||||
integrity sha512-AP1+fQIWSM/sMiET8fyayjx/J+JmTPt2Mr0FkrgqB4todtfa53sOsrSAcIrJRD5XS20bKUwaDIuMkWKCEiQLKA==
|
||||
version "20.2.7"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a"
|
||||
integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==
|
||||
|
||||
yargs@^16.1.0:
|
||||
version "16.2.0"
|
||||
|
Reference in New Issue
Block a user