bank20baht-cli 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -0
- package/dist/application/create.js +13 -2
- package/dist/application/generate.js +95 -0
- package/dist/args.js +1 -0
- package/dist/cli.js +41 -4
- package/dist/domain/create-plan.js +21 -3
- package/dist/domain/deploy-plan.js +82 -0
- package/dist/domain/remote-plan.js +156 -0
- package/package.json +4 -3
- package/templates/angular/README.md.hbs +46 -0
- package/templates/angular/_dependency-cruiser.mjs +51 -0
- package/templates/angular/apps/server/package.json +30 -0
- package/templates/angular/apps/server/src/app.ts +14 -0
- package/templates/angular/apps/server/src/index.ts +8 -0
- package/templates/angular/apps/server/test/app.test.ts +12 -0
- package/templates/angular/apps/server/tsconfig.json +7 -0
- package/templates/angular/apps/server/tsconfig.types.json +12 -0
- package/templates/angular/apps/shell/_editorconfig +17 -0
- package/templates/angular/apps/shell/angular.json +115 -0
- package/templates/angular/apps/shell/federation.config.mjs +44 -0
- package/templates/angular/apps/shell/package.json +42 -0
- package/templates/angular/apps/shell/public/federation.manifest.json +1 -0
- package/templates/angular/apps/shell/src/app/app.config.ts +20 -0
- package/templates/angular/apps/shell/src/app/app.css +4 -0
- package/templates/angular/apps/shell/src/app/app.html.hbs +16 -0
- package/templates/angular/apps/shell/src/app/app.routes.ts.hbs +39 -0
- package/templates/angular/apps/shell/src/app/app.ts +57 -0
- package/templates/angular/apps/shell/src/app/core/federation/load-remote.ts +31 -0
- package/templates/angular/apps/shell/src/app/core/federation/manifest.spec.ts +46 -0
- package/templates/angular/apps/shell/src/app/core/federation/manifest.ts +37 -0
- package/templates/angular/apps/shell/src/app/core/federation/remote-loader.ts +20 -0
- package/templates/angular/apps/shell/src/app/core/federation/timeout.ts +17 -0
- package/templates/angular/apps/shell/src/app/modules/home/home.component.ts.hbs +46 -0
- package/templates/angular/apps/shell/src/app/modules/unavailable/unavailable.component.ts +39 -0
- package/templates/angular/apps/shell/src/bootstrap.ts +25 -0
- package/templates/angular/apps/shell/src/environments/environment.development.ts +10 -0
- package/templates/angular/apps/shell/src/environments/environment.ts +10 -0
- package/templates/angular/apps/shell/src/index.html.hbs +12 -0
- package/templates/angular/apps/shell/src/main.ts +43 -0
- package/templates/angular/apps/shell/src/styles.css +19 -0
- package/templates/angular/apps/shell/tsconfig.app.json +10 -0
- package/templates/angular/apps/shell/tsconfig.federation.json +4 -0
- package/templates/angular/apps/shell/tsconfig.json.hbs +39 -0
- package/templates/angular/apps/shell/tsconfig.spec.json +9 -0
- package/templates/angular/bahtui.json +6 -0
- package/templates/angular/eslint.config.ts.hbs +28 -0
- package/templates/angular/knip.ts +27 -0
- package/templates/angular/package.json.hbs +52 -0
- package/templates/angular/packages/kit/package.json.hbs +27 -0
- package/templates/angular/packages/kit/src/api.ts +21 -0
- package/templates/angular/packages/kit/src/errors.ts +28 -0
- package/templates/angular/packages/kit/src/index.ts +2 -0
- package/templates/angular/packages/kit/test/errors.test.ts +28 -0
- package/templates/angular/packages/kit/tsconfig.json +9 -0
- package/templates/create/_gitignore +1 -0
- package/templates/create/apps/web/package.json.hbs +1 -1
- package/templates/create/package.json.hbs +1 -1
- package/templates/deploy/deploy/__project__.service.hbs +23 -0
- package/templates/deploy/deploy/env.example.hbs +4 -0
- package/templates/deploy/deploy/install.sh.hbs +58 -0
- package/templates/deploy/deploy/nginx.conf.hbs +67 -0
- package/templates/deploy/scripts/deploy.ts.hbs +33 -0
- package/templates/deploy/scripts/preview.ts.hbs +57 -0
- package/templates/deploy/scripts/release.ts.hbs +48 -0
- package/templates/new/_gitignore +3 -0
- package/templates/new/_prettierignore +1 -0
- package/templates/new/package.json.hbs +2 -2
- package/templates/remote/_editorconfig +17 -0
- package/templates/remote/angular.json.hbs +95 -0
- package/templates/remote/federation.config.mjs.hbs +48 -0
- package/templates/remote/package.json.hbs +37 -0
- package/templates/remote/src/app/__name__.routes.ts.hbs +12 -0
- package/templates/remote/src/app/modules/__name__/__name__.component.ts.hbs +37 -0
- package/templates/remote/src/app/services/__name__.service.ts.hbs +14 -0
- package/templates/remote/src/index.html.hbs +11 -0
- package/templates/remote/src/main.ts.hbs +3 -0
- package/templates/remote/tsconfig.app.json +10 -0
- package/templates/remote/tsconfig.federation.json +4 -0
- package/templates/remote/tsconfig.json.hbs +36 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# {{project}}: the web apps and their API behind one origin.
|
|
2
|
+
#
|
|
3
|
+
# / the host ({{host}})
|
|
4
|
+
# /remotes/<app>/ each remote, loaded by the host over Native Federation
|
|
5
|
+
# /federation.manifest.json which remotes exist; edit it to add or repoint one, no rebuild
|
|
6
|
+
# /api/ the Elysia server
|
|
7
|
+
#
|
|
8
|
+
# Installed as /etc/nginx/sites-available/{{project}} by deploy/install.sh.
|
|
9
|
+
|
|
10
|
+
upstream {{project}}_api {
|
|
11
|
+
server 127.0.0.1:{{apiPort}};
|
|
12
|
+
keepalive 16;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
server {
|
|
16
|
+
listen 80 default_server;
|
|
17
|
+
listen [::]:80 default_server;
|
|
18
|
+
server_name _;
|
|
19
|
+
|
|
20
|
+
root /var/www/{{project}}/{{host}};
|
|
21
|
+
index index.html;
|
|
22
|
+
|
|
23
|
+
gzip on;
|
|
24
|
+
gzip_types text/css application/javascript application/json image/svg+xml;
|
|
25
|
+
|
|
26
|
+
client_max_body_size 1m;
|
|
27
|
+
|
|
28
|
+
location = /federation.manifest.json {
|
|
29
|
+
alias /etc/{{project}}/federation.manifest.json;
|
|
30
|
+
add_header Cache-Control no-store;
|
|
31
|
+
# Repeated in every location that sets a header: nginx drops the inherited ones there.
|
|
32
|
+
add_header X-Content-Type-Options nosniff always;
|
|
33
|
+
add_header X-Frame-Options SAMEORIGIN always;
|
|
34
|
+
add_header Referrer-Policy same-origin always;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Entry points are re-checked on every load, so a new release shows up at once; the hashed
|
|
38
|
+
# chunks they point to never change and may be kept.
|
|
39
|
+
location /remotes/ {
|
|
40
|
+
root /var/www/{{project}};
|
|
41
|
+
try_files $uri =404;
|
|
42
|
+
add_header Cache-Control no-cache;
|
|
43
|
+
add_header X-Content-Type-Options nosniff always;
|
|
44
|
+
add_header X-Frame-Options SAMEORIGIN always;
|
|
45
|
+
add_header Referrer-Policy same-origin always;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
location /api/ {
|
|
49
|
+
proxy_pass http://{{project}}_api;
|
|
50
|
+
proxy_http_version 1.1;
|
|
51
|
+
proxy_set_header Connection "";
|
|
52
|
+
proxy_set_header Host $host;
|
|
53
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
54
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
55
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
56
|
+
add_header X-Content-Type-Options nosniff always;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# The host is a single-page app: any other path is its router's business.
|
|
60
|
+
location / {
|
|
61
|
+
try_files $uri $uri/ /index.html;
|
|
62
|
+
add_header Cache-Control no-cache;
|
|
63
|
+
add_header X-Content-Type-Options nosniff always;
|
|
64
|
+
add_header X-Frame-Options SAMEORIGIN always;
|
|
65
|
+
add_header Referrer-Policy same-origin always;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bun run deploy root@host` builds a release, copies it to the machine and installs it
|
|
3
|
+
* (deploy/install.sh: nginx, bun and the {{project}} user on the first run; the files and a
|
|
4
|
+
* restart on every run). The target is a Debian or Ubuntu machine you can SSH into as root.
|
|
5
|
+
* Pass `--no-build` to ship the `.release/` that is already there.
|
|
6
|
+
*/
|
|
7
|
+
import { RELEASE, buildRelease } from './release.ts'
|
|
8
|
+
|
|
9
|
+
const args = process.argv.slice(2)
|
|
10
|
+
const target = args.find((arg) => !arg.startsWith('--'))
|
|
11
|
+
if (target === undefined) {
|
|
12
|
+
console.error('usage: bun run deploy <user@host> [--no-build]')
|
|
13
|
+
process.exit(1)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const run = (command: string[]): void => {
|
|
17
|
+
console.log(`\x1b[36m$\x1b[0m ${command.join(' ')}`)
|
|
18
|
+
const result = Bun.spawnSync(command, { stdio: ['inherit', 'inherit', 'inherit'] })
|
|
19
|
+
if (result.exitCode !== 0) process.exit(result.exitCode)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!args.includes('--no-build')) buildRelease()
|
|
23
|
+
|
|
24
|
+
const REMOTE_DIR = '/opt/{{project}}-release'
|
|
25
|
+
// tar over ssh: the target needs nothing installed yet, not even rsync. Without macOS's extended
|
|
26
|
+
// attributes, which GNU tar on the other side only warns about.
|
|
27
|
+
run([
|
|
28
|
+
'sh',
|
|
29
|
+
'-c',
|
|
30
|
+
`COPYFILE_DISABLE=1 tar --no-xattrs -czf - -C ${RELEASE} . | ssh ${target} 'rm -rf ${REMOTE_DIR} && mkdir -p ${REMOTE_DIR} && tar -xzf - -C ${REMOTE_DIR}'`,
|
|
31
|
+
])
|
|
32
|
+
run(['ssh', target, `sh ${REMOTE_DIR}/install.sh`])
|
|
33
|
+
console.log(`deployed: http://${target.split('@').pop() ?? target}/`)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bun run preview` serves `.release/` the way deploy/nginx.conf does, on http://localhost:8080,
|
|
3
|
+
* with the API server started beside it: the production layout without a web server to install.
|
|
4
|
+
* Run `bun run release` first.
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync } from 'node:fs'
|
|
7
|
+
import { join } from 'node:path'
|
|
8
|
+
|
|
9
|
+
import { RELEASE } from './release.ts'
|
|
10
|
+
|
|
11
|
+
const PORT = 8080
|
|
12
|
+
const API_PORT = 3019
|
|
13
|
+
const API = `http://127.0.0.1:${String(API_PORT)}`
|
|
14
|
+
const WWW = join(RELEASE, 'www')
|
|
15
|
+
|
|
16
|
+
if (!existsSync(join(WWW, '{{host}}/index.html'))) {
|
|
17
|
+
console.error(`no ${RELEASE}/ yet: run \`bun run release\` first`)
|
|
18
|
+
process.exit(1)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const api = Bun.spawn(['bun', join(RELEASE, 'server/index.js')], {
|
|
22
|
+
env: { ...process.env, PORT: String(API_PORT) },
|
|
23
|
+
stdio: ['ignore', 'inherit', 'inherit'],
|
|
24
|
+
})
|
|
25
|
+
process.on('SIGINT', () => {
|
|
26
|
+
api.kill()
|
|
27
|
+
process.exit(0)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const file = async (path: string, cache: string): Promise<Response | undefined> => {
|
|
31
|
+
const found = Bun.file(path)
|
|
32
|
+
return (await found.exists())
|
|
33
|
+
? new Response(found, { headers: { 'cache-control': cache } })
|
|
34
|
+
: undefined
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const notFound = () => new Response('', { status: 404 })
|
|
38
|
+
|
|
39
|
+
Bun.serve({
|
|
40
|
+
port: PORT,
|
|
41
|
+
async fetch(request) {
|
|
42
|
+
const url = new URL(request.url)
|
|
43
|
+
const path = decodeURIComponent(url.pathname)
|
|
44
|
+
if (path.startsWith('/api/')) return fetch(new Request(`${API}${path}${url.search}`, request))
|
|
45
|
+
if (path.includes('..')) return new Response('bad path', { status: 400 })
|
|
46
|
+
if (path === '/federation.manifest.json') {
|
|
47
|
+
return (await file(join(RELEASE, 'federation.manifest.json'), 'no-store')) ?? notFound()
|
|
48
|
+
}
|
|
49
|
+
if (path.startsWith('/remotes/')) return (await file(join(WWW, path), 'no-cache')) ?? notFound()
|
|
50
|
+
return (
|
|
51
|
+
(path === '/' ? undefined : await file(join(WWW, '{{host}}', path), 'no-cache')) ??
|
|
52
|
+
(await file(join(WWW, '{{host}}/index.html'), 'no-cache')) ??
|
|
53
|
+
notFound()
|
|
54
|
+
)
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
console.log(`preview on http://localhost:${String(PORT)} (api on ${API})`)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bun run release` builds everything and lays it out the way the web server serves it, in
|
|
3
|
+
* `.release/`:
|
|
4
|
+
*
|
|
5
|
+
* server/index.js the API, one bundled file
|
|
6
|
+
* www/{{host}}/ the host
|
|
7
|
+
* www/remotes/<app>/ each remote in the host's federation manifest
|
|
8
|
+
* federation.manifest.json which remotes the host loads (installed once, then edited there)
|
|
9
|
+
* nginx.conf {{project}}.service env.example install.sh
|
|
10
|
+
*
|
|
11
|
+
* `scripts/deploy.ts` ships that folder; `scripts/preview.ts` serves it locally.
|
|
12
|
+
*/
|
|
13
|
+
import { cpSync, mkdirSync, readFileSync, rmSync } from 'node:fs'
|
|
14
|
+
import { join } from 'node:path'
|
|
15
|
+
|
|
16
|
+
export const RELEASE = '.release'
|
|
17
|
+
const HOST = 'apps/{{host}}'
|
|
18
|
+
const MANIFEST = join(HOST, 'public/federation.manifest.json')
|
|
19
|
+
|
|
20
|
+
const run = (command: string[]): void => {
|
|
21
|
+
console.log(`\x1b[36m$\x1b[0m ${command.join(' ')}`)
|
|
22
|
+
const result = Bun.spawnSync(command, { stdio: ['inherit', 'inherit', 'inherit'] })
|
|
23
|
+
if (result.exitCode !== 0) process.exit(result.exitCode)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const buildRelease = (): void => {
|
|
27
|
+
run(['bun', 'run', 'build'])
|
|
28
|
+
|
|
29
|
+
rmSync(RELEASE, { recursive: true, force: true })
|
|
30
|
+
mkdirSync(join(RELEASE, 'www/remotes'), { recursive: true })
|
|
31
|
+
mkdirSync(join(RELEASE, 'server'), { recursive: true })
|
|
32
|
+
|
|
33
|
+
cpSync('apps/server/dist/index.js', join(RELEASE, 'server/index.js'))
|
|
34
|
+
cpSync(join(HOST, 'dist/{{host}}/browser'), join(RELEASE, 'www/{{host}}'), { recursive: true })
|
|
35
|
+
const remotes = Object.keys(JSON.parse(readFileSync(MANIFEST, 'utf8')) as Record<string, unknown>)
|
|
36
|
+
for (const app of remotes) {
|
|
37
|
+
cpSync(`apps/${app}/dist/${app}/browser`, join(RELEASE, 'www/remotes', app), {
|
|
38
|
+
recursive: true,
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
cpSync(MANIFEST, join(RELEASE, 'federation.manifest.json'))
|
|
42
|
+
for (const file of ['nginx.conf', '{{project}}.service', 'env.example', 'install.sh']) {
|
|
43
|
+
cpSync(join('deploy', file), join(RELEASE, file))
|
|
44
|
+
}
|
|
45
|
+
console.log(`release ready in ${RELEASE}/`)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (import.meta.main) buildRelease()
|
package/templates/new/_gitignore
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"format": "prettier --write .",
|
|
16
16
|
"format:check": "prettier --check .",
|
|
17
17
|
"knip": "knip",
|
|
18
|
-
"madge": "madge --circular --extensions ts,tsx apps packages scripts",
|
|
18
|
+
"madge": "madge --circular --extensions ts,tsx --exclude '(^|/)(dist|node_modules|\\.angular)/' apps packages scripts",
|
|
19
19
|
"deps": "depcruise apps packages scripts --config .dependency-cruiser.mjs",
|
|
20
20
|
"dup": "jscpd",
|
|
21
21
|
"audit": "bun audit --audit-level=high",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@eslint/css": "^2.0.0",
|
|
34
34
|
"@eslint/js": "^10.0.1",
|
|
35
35
|
"@types/bun": "^1.3.0",
|
|
36
|
-
"bank20baht-strict": "0.0.
|
|
36
|
+
"bank20baht-strict": "0.0.6",
|
|
37
37
|
"dependency-cruiser": "^18.3.1",
|
|
38
38
|
"eslint": "^10.10.0",
|
|
39
39
|
"eslint-config-prettier": "^10.1.8",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Editor configuration, see https://editorconfig.org
|
|
2
|
+
root = true
|
|
3
|
+
|
|
4
|
+
[*]
|
|
5
|
+
charset = utf-8
|
|
6
|
+
indent_style = space
|
|
7
|
+
indent_size = 2
|
|
8
|
+
insert_final_newline = true
|
|
9
|
+
trim_trailing_whitespace = true
|
|
10
|
+
|
|
11
|
+
[*.ts]
|
|
12
|
+
quote_type = single
|
|
13
|
+
ij_typescript_use_double_quotes = false
|
|
14
|
+
|
|
15
|
+
[*.md]
|
|
16
|
+
max_line_length = off
|
|
17
|
+
trim_trailing_whitespace = false
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"cli": {
|
|
5
|
+
"packageManager": "bun"
|
|
6
|
+
},
|
|
7
|
+
"newProjectRoot": "projects",
|
|
8
|
+
"projects": {
|
|
9
|
+
"{{app}}": {
|
|
10
|
+
"projectType": "application",
|
|
11
|
+
"schematics": {},
|
|
12
|
+
"root": "",
|
|
13
|
+
"sourceRoot": "src",
|
|
14
|
+
"prefix": "{{name}}",
|
|
15
|
+
"architect": {
|
|
16
|
+
"build": {
|
|
17
|
+
"builder": "@angular-architects/native-federation:build",
|
|
18
|
+
"options": {
|
|
19
|
+
"cacheExternalArtifacts": true,
|
|
20
|
+
"tsConfig": "tsconfig.federation.json"
|
|
21
|
+
},
|
|
22
|
+
"configurations": {
|
|
23
|
+
"production": {
|
|
24
|
+
"target": "{{app}}:esbuild:production"
|
|
25
|
+
},
|
|
26
|
+
"development": {
|
|
27
|
+
"target": "{{app}}:esbuild:development",
|
|
28
|
+
"dev": true
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"defaultConfiguration": "production"
|
|
32
|
+
},
|
|
33
|
+
"serve": {
|
|
34
|
+
"builder": "@angular-architects/native-federation:build",
|
|
35
|
+
"options": {
|
|
36
|
+
"target": "{{app}}:serve-original:development",
|
|
37
|
+
"tsConfig": "tsconfig.federation.json",
|
|
38
|
+
"rebuildDelay": 500,
|
|
39
|
+
"cacheExternalArtifacts": true,
|
|
40
|
+
"dev": true,
|
|
41
|
+
"devServer": true,
|
|
42
|
+
"port": 0
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"esbuild": {
|
|
46
|
+
"builder": "@angular/build:application",
|
|
47
|
+
"options": {
|
|
48
|
+
"browser": "src/main.ts",
|
|
49
|
+
"tsConfig": "tsconfig.app.json",
|
|
50
|
+
"styles": [],
|
|
51
|
+
"polyfills": ["es-module-shims"]
|
|
52
|
+
},
|
|
53
|
+
"configurations": {
|
|
54
|
+
"production": {
|
|
55
|
+
"budgets": [
|
|
56
|
+
{
|
|
57
|
+
"type": "initial",
|
|
58
|
+
"maximumWarning": "500kB",
|
|
59
|
+
"maximumError": "1MB"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"type": "anyComponentStyle",
|
|
63
|
+
"maximumWarning": "4kB",
|
|
64
|
+
"maximumError": "8kB"
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
"outputHashing": "all"
|
|
68
|
+
},
|
|
69
|
+
"development": {
|
|
70
|
+
"optimization": false,
|
|
71
|
+
"extractLicenses": false,
|
|
72
|
+
"sourceMap": true
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"defaultConfiguration": "production"
|
|
76
|
+
},
|
|
77
|
+
"serve-original": {
|
|
78
|
+
"builder": "@angular/build:dev-server",
|
|
79
|
+
"configurations": {
|
|
80
|
+
"production": {
|
|
81
|
+
"buildTarget": "{{app}}:esbuild:production"
|
|
82
|
+
},
|
|
83
|
+
"development": {
|
|
84
|
+
"buildTarget": "{{app}}:esbuild:development"
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"defaultConfiguration": "development",
|
|
88
|
+
"options": {
|
|
89
|
+
"port": {{port}}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { withNativeFederation, fromPackageJson } from '@angular-architects/native-federation/config'
|
|
2
|
+
|
|
3
|
+
export default withNativeFederation({
|
|
4
|
+
name: '{{app}}',
|
|
5
|
+
|
|
6
|
+
shared: fromPackageJson({
|
|
7
|
+
singleton: true,
|
|
8
|
+
strictVersion: true,
|
|
9
|
+
requiredVersion: 'auto',
|
|
10
|
+
build: 'package',
|
|
11
|
+
})
|
|
12
|
+
// includeSecondaries is an opt-out of ignoreUnusedDeps, so all of
|
|
13
|
+
// @angular/core is shared to prevent mismatches.
|
|
14
|
+
.patch(['@angular/core'], { includeSecondaries: { keepAll: true } })
|
|
15
|
+
// Same for bank20baht-ui: the host and the remotes may import different entry
|
|
16
|
+
// points of it. Unshared, each side bundles its own copy of the elements and
|
|
17
|
+
// the second customElements.define('baht-button') throws.
|
|
18
|
+
.patch(['bank20baht-ui'], { includeSecondaries: { keepAll: true } }),
|
|
19
|
+
|
|
20
|
+
skip: [
|
|
21
|
+
'rxjs/ajax',
|
|
22
|
+
'rxjs/fetch',
|
|
23
|
+
'rxjs/testing',
|
|
24
|
+
'rxjs/webSocket',
|
|
25
|
+
// Server / React entry points of bank20baht-ui (pulled in by keepAll above).
|
|
26
|
+
'bank20baht-ui/ssr',
|
|
27
|
+
'bank20baht-ui/hydrate',
|
|
28
|
+
'bank20baht-ui/react',
|
|
29
|
+
// marked's command-line entry imports Node built-ins.
|
|
30
|
+
/^marked\/bin/,
|
|
31
|
+
],
|
|
32
|
+
|
|
33
|
+
// The kit (a tsconfig path into packages/kit) is compiled into each app, not shared.
|
|
34
|
+
sharedMappings: [],
|
|
35
|
+
|
|
36
|
+
exposes: {
|
|
37
|
+
'./Routes': './src/app/{{name}}.routes.ts',
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
features: {
|
|
41
|
+
// Off: the unused-dependency scan (sheriff) refuses sources outside the app folder, and
|
|
42
|
+
// the kit lives in packages/kit. Every package.json dependency is shared instead, so each
|
|
43
|
+
// app lists bank20baht-ui's optional peers (marked, dompurify, flatpickr) itself.
|
|
44
|
+
ignoreUnusedDeps: false,
|
|
45
|
+
// Groups chunks in remoteEntry.json for a smaller metadata file.
|
|
46
|
+
denseChunking: true,
|
|
47
|
+
},
|
|
48
|
+
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{app}}",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"ng": "ng",
|
|
7
|
+
"dev": "ng serve",
|
|
8
|
+
"build": "ng build",
|
|
9
|
+
"typecheck": "tsc -b --noEmit",
|
|
10
|
+
"pretypecheck": "bun run --filter server build:types",
|
|
11
|
+
"prebuild": "bun run --filter server build:types"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@angular-architects/native-federation": "^22.2.1",
|
|
15
|
+
"@angular/common": "^22.2.0",
|
|
16
|
+
"@angular/compiler": "^22.2.0",
|
|
17
|
+
"@angular/core": "^22.2.0",
|
|
18
|
+
"@angular/platform-browser": "^22.2.0",
|
|
19
|
+
"@angular/router": "^22.2.0",
|
|
20
|
+
"bank20baht-ui": "0.19.0",
|
|
21
|
+
"bank20baht-ui-angular": "0.2.0",
|
|
22
|
+
"dompurify": "3.4.17",
|
|
23
|
+
"es-module-shims": "^2.8.0",
|
|
24
|
+
"flatpickr": "4.6.13",
|
|
25
|
+
"marked": "18.1.3",
|
|
26
|
+
"ngx-eden": "^0.2.0",
|
|
27
|
+
"rxjs": "~7.8.0",
|
|
28
|
+
"tslib": "^2.3.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@angular/build": "^22.2.0",
|
|
32
|
+
"@angular/cli": "^22.2.0",
|
|
33
|
+
"@angular/compiler-cli": "^22.2.0",
|
|
34
|
+
"@softarc/native-federation-orchestrator": "^4.6.0",
|
|
35
|
+
"typescript": "~6.0.2"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Routes } from '@angular/router'
|
|
2
|
+
|
|
3
|
+
import { {{pascal}}Component } from './modules/{{name}}/{{name}}.component'
|
|
4
|
+
|
|
5
|
+
/** What the host mounts under `/{{name}}` (exposed as `./Routes`). */
|
|
6
|
+
export const routes: Routes = [
|
|
7
|
+
{
|
|
8
|
+
path: '',
|
|
9
|
+
title: '{{title}}',
|
|
10
|
+
component: {{pascal}}Component,
|
|
11
|
+
},
|
|
12
|
+
]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
|
2
|
+
import { describeError } from '@{{project}}/kit'
|
|
3
|
+
import { BAHT_UI } from 'bank20baht-ui-angular'
|
|
4
|
+
import { {{pascal}}Service } from '../../services/{{name}}.service'
|
|
5
|
+
|
|
6
|
+
/** The {{title}} page: what the server says at `GET /api/hello`. */
|
|
7
|
+
@Component({
|
|
8
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
9
|
+
imports: [BAHT_UI],
|
|
10
|
+
selector: '{{name}}-page',
|
|
11
|
+
template: `
|
|
12
|
+
<div class="app-page">
|
|
13
|
+
<baht-page-header
|
|
14
|
+
heading="{{title}}"
|
|
15
|
+
description="A remote, loaded by the host at runtime."
|
|
16
|
+
></baht-page-header>
|
|
17
|
+
<baht-card heading="From the server">
|
|
18
|
+
@if (hello.error(); as error) {
|
|
19
|
+
<baht-error-state
|
|
20
|
+
heading="The server did not answer"
|
|
21
|
+
[message]="describeError(error)"
|
|
22
|
+
retry-label="Try again"
|
|
23
|
+
(bahtRetry)="hello.reload()"
|
|
24
|
+
></baht-error-state>
|
|
25
|
+
} @else if (hello.value(); as body) {
|
|
26
|
+
<p [textContent]="body.message"></p>
|
|
27
|
+
} @else {
|
|
28
|
+
<baht-skeleton variant="text"></baht-skeleton>
|
|
29
|
+
}
|
|
30
|
+
</baht-card>
|
|
31
|
+
</div>
|
|
32
|
+
`,
|
|
33
|
+
})
|
|
34
|
+
export class {{pascal}}Component {
|
|
35
|
+
protected readonly hello = inject({{pascal}}Service).hello()
|
|
36
|
+
protected readonly describeError = describeError
|
|
37
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Injectable, inject } from '@angular/core'
|
|
2
|
+
import { Api } from '@{{project}}/kit'
|
|
3
|
+
import { edenResource } from 'ngx-eden'
|
|
4
|
+
|
|
5
|
+
/** Everything the {{title}} pages ask the server. */
|
|
6
|
+
@Injectable({ providedIn: 'root' })
|
|
7
|
+
export class {{pascal}}Service {
|
|
8
|
+
private readonly api = inject(Api).client.api
|
|
9
|
+
|
|
10
|
+
/** `GET /api/hello` as a resource; create it in an injection context (a field initializer). */
|
|
11
|
+
hello() {
|
|
12
|
+
return edenResource(() => this.api.hello.get.$request())
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<title>{{app}} (remote)</title>
|
|
6
|
+
<meta name="robots" content="noindex" />
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<p>{{app}} is a Native Federation remote. Open it through the host.</p>
|
|
10
|
+
</body>
|
|
11
|
+
</html>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
|
2
|
+
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
|
3
|
+
{
|
|
4
|
+
"extends": "./tsconfig.json",
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"types": []
|
|
7
|
+
},
|
|
8
|
+
"include": ["src/**/*.ts"],
|
|
9
|
+
"exclude": ["src/**/*.spec.ts"]
|
|
10
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
|
2
|
+
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
|
3
|
+
{
|
|
4
|
+
"compileOnSave": false,
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"strict": true,
|
|
7
|
+
"noUncheckedIndexedAccess": true,
|
|
8
|
+
"exactOptionalPropertyTypes": true,
|
|
9
|
+
"verbatimModuleSyntax": true,
|
|
10
|
+
"noImplicitOverride": true,
|
|
11
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
12
|
+
"noImplicitReturns": true,
|
|
13
|
+
"noFallthroughCasesInSwitch": true,
|
|
14
|
+
"skipLibCheck": true,
|
|
15
|
+
"isolatedModules": true,
|
|
16
|
+
"experimentalDecorators": true,
|
|
17
|
+
"importHelpers": true,
|
|
18
|
+
"target": "ES2022",
|
|
19
|
+
"module": "preserve",
|
|
20
|
+
"paths": {
|
|
21
|
+
"@{{project}}/kit": ["../../packages/kit/src/index.ts"]
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"angularCompilerOptions": {
|
|
25
|
+
"enableI18nLegacyMessageIdFormat": false,
|
|
26
|
+
"strictInjectionParameters": true,
|
|
27
|
+
"strictTemplates": true,
|
|
28
|
+
"strictInputAccessModifiers": true
|
|
29
|
+
},
|
|
30
|
+
"files": [],
|
|
31
|
+
"references": [
|
|
32
|
+
{
|
|
33
|
+
"path": "./tsconfig.app.json"
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|