@bleedingdev/modern-js-create 3.2.0-ultramodern.8 → 3.2.0-ultramodern.81
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 +143 -35
- package/dist/index.js +4587 -607
- package/dist/types/locale/en.d.ts +3 -0
- package/dist/types/locale/zh.d.ts +3 -0
- package/dist/types/ultramodern-workspace.d.ts +11 -0
- package/package.json +6 -6
- package/template/.codex/hooks.json +16 -0
- package/template/.github/renovate.json +53 -0
- package/template/.github/workflows/ultramodern-gates.yml.handlebars +34 -10
- package/template/.mise.toml.handlebars +2 -0
- package/template/AGENTS.md +9 -6
- package/template/README.md +60 -34
- package/template/api/effect/index.ts.handlebars +9 -15
- package/template/config/public/locales/cs/translation.json +39 -0
- package/template/config/public/locales/en/translation.json +39 -0
- package/template/lefthook.yml +10 -0
- package/template/modern.config.ts.handlebars +41 -21
- package/template/oxfmt.config.ts +4 -3
- package/template/oxlint.config.ts +4 -4
- package/template/package.json.handlebars +43 -34
- package/template/pnpm-workspace.yaml +19 -0
- package/template/rstest.config.mts +7 -0
- package/template/scripts/bootstrap-agent-skills.mjs +63 -31
- package/template/scripts/check-i18n-strings.mjs +83 -0
- package/template/scripts/validate-ultramodern.mjs.handlebars +373 -35
- package/template/shared/effect/api.ts.handlebars +1 -2
- package/template/src/modern-app-env.d.ts +2 -0
- package/template/src/modern.runtime.ts.handlebars +17 -3
- package/template/src/routes/[lang]/page.tsx.handlebars +212 -0
- package/template/src/routes/index.css.handlebars +14 -3
- package/template/src/routes/layout.tsx.handlebars +2 -1
- package/template/tests/tsconfig.json +7 -0
- package/template/tests/ultramodern.contract.test.ts.handlebars +78 -0
- package/template-workspace/.agents/agent-reference-repos.json +24 -0
- package/template-workspace/.agents/skills-lock.json +19 -0
- package/template-workspace/.codex/hooks.json +16 -0
- package/template-workspace/.github/renovate.json +29 -0
- package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +54 -0
- package/template-workspace/.gitignore.handlebars +5 -0
- package/template-workspace/.mise.toml.handlebars +2 -0
- package/template-workspace/AGENTS.md +36 -5
- package/template-workspace/README.md.handlebars +33 -10
- package/template-workspace/lefthook.yml +10 -0
- package/template-workspace/oxfmt.config.ts +13 -3
- package/template-workspace/oxlint.config.ts +12 -4
- package/template-workspace/pnpm-workspace.yaml +20 -8
- package/template-workspace/scripts/bootstrap-agent-skills.mjs +79 -22
- package/template-workspace/scripts/setup-agent-reference-repos.mjs +368 -0
- package/template/src/routes/page.tsx.handlebars +0 -119
- package/template-workspace/scripts/validate-ultramodern-workspace.mjs.handlebars +0 -403
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { Helmet } from '@modern-js/runtime/head';
|
|
2
|
+
import { useModernI18n } from '@modern-js/plugin-i18n/runtime';
|
|
3
|
+
import { useLocation } from '{{routerRuntimeImport}}';
|
|
4
|
+
{{#if useEffectBff}}import effectBff from '@api/effect/index';
|
|
5
|
+
import { Effect } from '@modern-js/plugin-bff/effect-client';
|
|
6
|
+
import { useEffect, useState } from 'react';
|
|
7
|
+
{{/if}}
|
|
8
|
+
import { useTranslation } from 'react-i18next';
|
|
9
|
+
|
|
10
|
+
const fallbackLanguage = 'en';
|
|
11
|
+
const supportedLanguages = ['en', 'cs'] as const;
|
|
12
|
+
type SupportedLanguage = (typeof supportedLanguages)[number];
|
|
13
|
+
|
|
14
|
+
const isSupportedLanguage = (value: string): value is SupportedLanguage =>
|
|
15
|
+
supportedLanguages.includes(value as SupportedLanguage);
|
|
16
|
+
|
|
17
|
+
const stripLanguagePrefix = (pathname: string) => {
|
|
18
|
+
const segments = pathname.split('/').filter(Boolean);
|
|
19
|
+
if (segments.length > 0 && isSupportedLanguage(segments[0] ?? '')) {
|
|
20
|
+
segments.shift();
|
|
21
|
+
}
|
|
22
|
+
return `/${segments.join('/')}`;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const localizedPath = (pathname: string, language: SupportedLanguage) => {
|
|
26
|
+
const pathWithoutLanguage = stripLanguagePrefix(pathname);
|
|
27
|
+
return pathWithoutLanguage === '/' ? `/${language}` : `/${language}${pathWithoutLanguage}`;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const absoluteUrl = (pathname: string) => {
|
|
31
|
+
const origin = ULTRAMODERN_SITE_URL.replace(/\/+$/u, '');
|
|
32
|
+
return `${origin}${pathname}`;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const locationSuffix = (location: { hash?: unknown; search?: unknown; searchStr?: unknown }) => {
|
|
36
|
+
const { hash, search, searchStr } = location;
|
|
37
|
+
let locationSearch = '';
|
|
38
|
+
if (typeof searchStr === 'string') {
|
|
39
|
+
locationSearch = searchStr;
|
|
40
|
+
} else if (typeof search === 'string') {
|
|
41
|
+
locationSearch = search;
|
|
42
|
+
}
|
|
43
|
+
const locationHash = typeof hash === 'string' ? hash : '';
|
|
44
|
+
return `${locationSearch}${locationHash}`;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const Index = () => {
|
|
48
|
+
const { t } = useTranslation();
|
|
49
|
+
const { language } = useModernI18n();
|
|
50
|
+
const location = useLocation();
|
|
51
|
+
const currentLanguage = isSupportedLanguage(language) ? language : fallbackLanguage;
|
|
52
|
+
const canonicalPath = localizedPath(location.pathname, currentLanguage);
|
|
53
|
+
const suffix = locationSuffix(location);
|
|
54
|
+
const languageOptions = supportedLanguages.map((code) => ({
|
|
55
|
+
code,
|
|
56
|
+
href: `${localizedPath(location.pathname, code)}${suffix}`,
|
|
57
|
+
label: t(`home.language.${code}`),
|
|
58
|
+
}));
|
|
59
|
+
{{#if useEffectBff}} const [effectMessage, setEffectMessage] = useState('loading...');
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
let mounted = true;
|
|
63
|
+
Effect.runFork(
|
|
64
|
+
Effect.promise(() => effectBff.client.greetings.hello({})).pipe(
|
|
65
|
+
Effect.tap((data) =>
|
|
66
|
+
Effect.sync(() => {
|
|
67
|
+
if (mounted) {
|
|
68
|
+
setEffectMessage(data.message);
|
|
69
|
+
}
|
|
70
|
+
}),
|
|
71
|
+
),
|
|
72
|
+
),
|
|
73
|
+
);
|
|
74
|
+
return () => {
|
|
75
|
+
mounted = false;
|
|
76
|
+
};
|
|
77
|
+
}, []);
|
|
78
|
+
{{/if}}
|
|
79
|
+
return (
|
|
80
|
+
<div className="container-box">
|
|
81
|
+
<Helmet>
|
|
82
|
+
<link
|
|
83
|
+
rel="icon"
|
|
84
|
+
type="image/x-icon"
|
|
85
|
+
href="https://lf3-static.bytednsdoc.com/obj/eden-cn/uhbfnupenuhf/favicon.ico"
|
|
86
|
+
/>
|
|
87
|
+
<link rel="canonical" href={absoluteUrl(canonicalPath)} />
|
|
88
|
+
{supportedLanguages.map((code) => (
|
|
89
|
+
<link
|
|
90
|
+
href={absoluteUrl(localizedPath(location.pathname, code))}
|
|
91
|
+
hrefLang={code}
|
|
92
|
+
key={code}
|
|
93
|
+
rel="alternate"
|
|
94
|
+
/>
|
|
95
|
+
))}
|
|
96
|
+
<link
|
|
97
|
+
href={absoluteUrl(localizedPath(location.pathname, fallbackLanguage))}
|
|
98
|
+
hrefLang="x-default"
|
|
99
|
+
rel="alternate"
|
|
100
|
+
/>
|
|
101
|
+
</Helmet>
|
|
102
|
+
<main>
|
|
103
|
+
<nav className="language-switcher" aria-label={t('home.language.switcher')}>
|
|
104
|
+
{languageOptions.map((option) => (
|
|
105
|
+
<a
|
|
106
|
+
aria-current={currentLanguage === option.code ? 'page' : undefined}
|
|
107
|
+
href={option.href}
|
|
108
|
+
key={option.code}
|
|
109
|
+
>
|
|
110
|
+
{option.label}
|
|
111
|
+
</a>
|
|
112
|
+
))}
|
|
113
|
+
</nav>
|
|
114
|
+
<div className="title">
|
|
115
|
+
{t('home.title')}
|
|
116
|
+
<img
|
|
117
|
+
alt={t('home.logoAlt')}
|
|
118
|
+
className="logo"
|
|
119
|
+
src="https://lf3-static.bytednsdoc.com/obj/eden-cn/zq-uylkvT/ljhwZthlaukjlkulzlp/modern-js-logo.svg"
|
|
120
|
+
/>
|
|
121
|
+
<p className="name">{t('home.name')}</p>
|
|
122
|
+
</div>
|
|
123
|
+
<p className="description{{#if enableTailwind}} text-emerald-700 font-semibold{{/if}}">
|
|
124
|
+
{t('home.description.intro')} <code className="code">presetUltramodern(...)</code>{' '}
|
|
125
|
+
{/* i18n-ignore technical token */}
|
|
126
|
+
{t('home.description.afterPreset')}{' '}
|
|
127
|
+
<code className="code">modern.config.ts</code>
|
|
128
|
+
{/* i18n-ignore technical token */}
|
|
129
|
+
{' '}
|
|
130
|
+
{t('home.description.afterConfig')}{' '}
|
|
131
|
+
<code className="code">pnpm run ultramodern:check</code>
|
|
132
|
+
{/* i18n-ignore technical token */}
|
|
133
|
+
{' '}
|
|
134
|
+
{t('home.description.end')}
|
|
135
|
+
</p>
|
|
136
|
+
{{#if useEffectBff}}
|
|
137
|
+
<p className="description effect-message{{#if enableTailwind}} text-emerald-700 font-semibold{{/if}}">
|
|
138
|
+
{t('home.bff.response')} <code className="code">{effectMessage}</code>
|
|
139
|
+
</p>
|
|
140
|
+
{{/if}}
|
|
141
|
+
<div className="grid">
|
|
142
|
+
<a
|
|
143
|
+
href="https://bleedingdev.github.io/ultramodern.js/guides/get-started/ultramodern.html"
|
|
144
|
+
target="_blank"
|
|
145
|
+
rel="noopener noreferrer"
|
|
146
|
+
className="card"
|
|
147
|
+
>
|
|
148
|
+
<h2>
|
|
149
|
+
{t('home.cards.guide.title')}
|
|
150
|
+
<img
|
|
151
|
+
alt=""
|
|
152
|
+
className="arrow-right"
|
|
153
|
+
src="https://lf3-static.bytednsdoc.com/obj/eden-cn/zq-uylkvT/ljhwZthlaukjlkulzlp/arrow-right.svg"
|
|
154
|
+
/>
|
|
155
|
+
</h2>
|
|
156
|
+
<p>{t('home.cards.guide.body')}</p>
|
|
157
|
+
</a>
|
|
158
|
+
<a
|
|
159
|
+
href="https://bleedingdev.github.io/ultramodern.js/configure/app/usage.html"
|
|
160
|
+
target="_blank"
|
|
161
|
+
className="card"
|
|
162
|
+
rel="noreferrer"
|
|
163
|
+
>
|
|
164
|
+
<h2>
|
|
165
|
+
{t('home.cards.config.title')}
|
|
166
|
+
<img
|
|
167
|
+
alt=""
|
|
168
|
+
className="arrow-right"
|
|
169
|
+
src="https://lf3-static.bytednsdoc.com/obj/eden-cn/zq-uylkvT/ljhwZthlaukjlkulzlp/arrow-right.svg"
|
|
170
|
+
/>
|
|
171
|
+
</h2>
|
|
172
|
+
<p>{t('home.cards.config.body')}</p>
|
|
173
|
+
</a>
|
|
174
|
+
<a
|
|
175
|
+
href="https://github.com/BleedingDev/ultramodern.js/blob/main-ultramodern/packages/toolkit/create/template/.github/workflows/ultramodern-gates.yml.handlebars"
|
|
176
|
+
target="_blank"
|
|
177
|
+
className="card"
|
|
178
|
+
rel="noreferrer"
|
|
179
|
+
>
|
|
180
|
+
<h2>
|
|
181
|
+
{t('home.cards.gates.title')}
|
|
182
|
+
<img
|
|
183
|
+
alt=""
|
|
184
|
+
className="arrow-right"
|
|
185
|
+
src="https://lf3-static.bytednsdoc.com/obj/eden-cn/zq-uylkvT/ljhwZthlaukjlkulzlp/arrow-right.svg"
|
|
186
|
+
/>
|
|
187
|
+
</h2>
|
|
188
|
+
<p>{t('home.cards.gates.body')}</p>
|
|
189
|
+
</a>
|
|
190
|
+
<a
|
|
191
|
+
href="https://bleedingdev.github.io/ultramodern.js/configure/app/bff/effect.html"
|
|
192
|
+
target="_blank"
|
|
193
|
+
rel="noopener noreferrer"
|
|
194
|
+
className="card"
|
|
195
|
+
>
|
|
196
|
+
<h2>
|
|
197
|
+
{t('home.cards.bff.title')}
|
|
198
|
+
<img
|
|
199
|
+
alt=""
|
|
200
|
+
className="arrow-right"
|
|
201
|
+
src="https://lf3-static.bytednsdoc.com/obj/eden-cn/zq-uylkvT/ljhwZthlaukjlkulzlp/arrow-right.svg"
|
|
202
|
+
/>
|
|
203
|
+
</h2>
|
|
204
|
+
<p>{t('home.cards.bff.body')}</p>
|
|
205
|
+
</a>
|
|
206
|
+
</div>
|
|
207
|
+
</main>
|
|
208
|
+
</div>
|
|
209
|
+
);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
export default Index;
|
|
@@ -4,7 +4,12 @@
|
|
|
4
4
|
body {
|
|
5
5
|
padding: 0;
|
|
6
6
|
margin: 0;
|
|
7
|
-
font-family:
|
|
7
|
+
font-family:
|
|
8
|
+
PingFang SC,
|
|
9
|
+
Hiragino Sans GB,
|
|
10
|
+
Microsoft YaHei,
|
|
11
|
+
Arial,
|
|
12
|
+
sans-serif;
|
|
8
13
|
background: linear-gradient(to bottom, transparent, #fff) #eceeef;
|
|
9
14
|
}
|
|
10
15
|
|
|
@@ -67,8 +72,14 @@ main {
|
|
|
67
72
|
padding: 0.6rem 0.9rem;
|
|
68
73
|
font-size: 1.05rem;
|
|
69
74
|
font-family:
|
|
70
|
-
Menlo,
|
|
71
|
-
|
|
75
|
+
Menlo,
|
|
76
|
+
Monaco,
|
|
77
|
+
Lucida Console,
|
|
78
|
+
Liberation Mono,
|
|
79
|
+
DejaVu Sans Mono,
|
|
80
|
+
Bitstream Vera Sans Mono,
|
|
81
|
+
Courier New,
|
|
82
|
+
monospace;
|
|
72
83
|
}
|
|
73
84
|
|
|
74
85
|
.container-box .grid {
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { describe, expect, test } from '@rstest/core';
|
|
4
|
+
|
|
5
|
+
const root = process.cwd();
|
|
6
|
+
const readText = (relativePath: string) =>
|
|
7
|
+
fs.readFileSync(path.join(root, relativePath), 'utf-8');
|
|
8
|
+
const readJson = <T>(relativePath: string): T =>
|
|
9
|
+
JSON.parse(readText(relativePath)) as T;
|
|
10
|
+
|
|
11
|
+
describe('generated UltraModern contract', () => {
|
|
12
|
+
test('keeps localized route metadata and Rstest wiring', () => {
|
|
13
|
+
expect(fs.existsSync(path.join(root, 'src/routes/[lang]/page.tsx'))).toBe(
|
|
14
|
+
true,
|
|
15
|
+
);
|
|
16
|
+
expect(fs.existsSync(path.join(root, 'src/routes/page.tsx'))).toBe(false);
|
|
17
|
+
expect(fs.existsSync(path.join(root, 'src/routes/layout.tsx'))).toBe(true);
|
|
18
|
+
{{#if enableTailwind}}
|
|
19
|
+
expect(fs.existsSync(path.join(root, 'postcss.config.mjs'))).toBe(true);
|
|
20
|
+
expect(fs.existsSync(path.join(root, 'tailwind.config.ts'))).toBe(true);
|
|
21
|
+
{{/if}}
|
|
22
|
+
{{#unless enableTailwind}}
|
|
23
|
+
expect(fs.existsSync(path.join(root, 'postcss.config.mjs'))).toBe(false);
|
|
24
|
+
expect(fs.existsSync(path.join(root, 'tailwind.config.ts'))).toBe(false);
|
|
25
|
+
{{/unless}}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('retains package-source metadata for generated Modern.js packages', () => {
|
|
29
|
+
const packageJson = readJson<{
|
|
30
|
+
dependencies?: Record<string, string>;
|
|
31
|
+
devDependencies?: Record<string, string>;
|
|
32
|
+
modernjs?: {
|
|
33
|
+
packageSource?: {
|
|
34
|
+
config?: string;
|
|
35
|
+
};
|
|
36
|
+
preset?: string;
|
|
37
|
+
};
|
|
38
|
+
}>('package.json');
|
|
39
|
+
const packageSource = readJson<{
|
|
40
|
+
modernPackages?: {
|
|
41
|
+
packages?: string[];
|
|
42
|
+
specifier?: string;
|
|
43
|
+
};
|
|
44
|
+
strategy?: string;
|
|
45
|
+
}>('.modernjs/ultramodern-package-source.json');
|
|
46
|
+
|
|
47
|
+
expect(packageJson.modernjs?.preset).toBe('presetUltramodern');
|
|
48
|
+
expect(packageJson.modernjs?.packageSource?.config).toBe(
|
|
49
|
+
'./.modernjs/ultramodern-package-source.json',
|
|
50
|
+
);
|
|
51
|
+
expect(packageSource.strategy).toMatch(/^(workspace|install)$/u);
|
|
52
|
+
expect(packageSource.modernPackages?.packages).toContain(
|
|
53
|
+
'@modern-js/runtime',
|
|
54
|
+
);
|
|
55
|
+
expect(packageSource.modernPackages?.packages).toContain(
|
|
56
|
+
'@modern-js/app-tools',
|
|
57
|
+
);
|
|
58
|
+
expect(packageSource.modernPackages?.packages).toContain(
|
|
59
|
+
'@modern-js/adapter-rstest',
|
|
60
|
+
);
|
|
61
|
+
expect(packageSource.modernPackages?.specifier).toBeTruthy();
|
|
62
|
+
expect(
|
|
63
|
+
packageJson.devDependencies?.['@modern-js/adapter-rstest'],
|
|
64
|
+
).toBeTruthy();
|
|
65
|
+
{{#if enableTailwind}}
|
|
66
|
+
expect(packageJson.devDependencies?.tailwindcss).toBe('^4.3.0');
|
|
67
|
+
expect(packageJson.devDependencies?.['@tailwindcss/postcss']).toBe(
|
|
68
|
+
'^4.3.0',
|
|
69
|
+
);
|
|
70
|
+
{{/if}}
|
|
71
|
+
{{#unless enableTailwind}}
|
|
72
|
+
expect(packageJson.devDependencies?.tailwindcss).toBeUndefined();
|
|
73
|
+
expect(
|
|
74
|
+
packageJson.devDependencies?.['@tailwindcss/postcss'],
|
|
75
|
+
).toBeUndefined();
|
|
76
|
+
{{/unless}}
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"defaultEnabled": true,
|
|
4
|
+
"strategy": "git-subtree-squash",
|
|
5
|
+
"installDir": "repos",
|
|
6
|
+
"repositories": [
|
|
7
|
+
{
|
|
8
|
+
"id": "effect",
|
|
9
|
+
"name": "Effect",
|
|
10
|
+
"url": "https://github.com/Effect-TS/effect.git",
|
|
11
|
+
"ref": "main",
|
|
12
|
+
"path": "repos/effect",
|
|
13
|
+
"readOnly": true
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "ultramodern-js",
|
|
17
|
+
"name": "UltraModern.js",
|
|
18
|
+
"url": "https://github.com/BleedingDev/ultramodern.js.git",
|
|
19
|
+
"ref": "main-ultramodern",
|
|
20
|
+
"path": "repos/ultramodern.js",
|
|
21
|
+
"readOnly": true
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -17,6 +17,20 @@
|
|
|
17
17
|
"licensePath": ".agents/rstackjs-agent-skills-LICENSE",
|
|
18
18
|
"install": "vendored"
|
|
19
19
|
},
|
|
20
|
+
{
|
|
21
|
+
"id": "module-federation-agent-skills",
|
|
22
|
+
"visibility": "public",
|
|
23
|
+
"repository": "https://github.com/module-federation/agent-skills",
|
|
24
|
+
"commit": "07bb5b6c43ad457609e00c081b72d4c42508ec76",
|
|
25
|
+
"install": "clone",
|
|
26
|
+
"baseline": [
|
|
27
|
+
{
|
|
28
|
+
"name": "mf",
|
|
29
|
+
"path": ".agents/skills/mf",
|
|
30
|
+
"reason": "Module Federation docs, config inspection, type checking, shared dependency checks, and observability troubleshooting"
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
},
|
|
20
34
|
{
|
|
21
35
|
"id": "techsiocz-private",
|
|
22
36
|
"visibility": "private",
|
|
@@ -81,6 +95,11 @@
|
|
|
81
95
|
"name": "rstest-best-practices",
|
|
82
96
|
"path": ".agents/skills/rstest-best-practices",
|
|
83
97
|
"reason": "Rstest configuration, test writing, mocking, snapshots, coverage, and CI behavior"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"name": "mf",
|
|
101
|
+
"path": ".agents/skills/mf",
|
|
102
|
+
"reason": "Module Federation docs, config inspection, type checking, shared dependency checks, and observability troubleshooting"
|
|
84
103
|
}
|
|
85
104
|
],
|
|
86
105
|
"excludedByDefault": [
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Stop": [
|
|
3
|
+
{
|
|
4
|
+
"command": "pnpm format && pnpm lint:fix && pnpm check",
|
|
5
|
+
"timeout": 600000,
|
|
6
|
+
"statusMessage": "Running UltraModern quality gates"
|
|
7
|
+
}
|
|
8
|
+
],
|
|
9
|
+
"SubagentStop": [
|
|
10
|
+
{
|
|
11
|
+
"command": "pnpm format && pnpm lint:fix && pnpm check",
|
|
12
|
+
"timeout": 600000,
|
|
13
|
+
"statusMessage": "Running UltraModern quality gates"
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
3
|
+
"extends": ["config:recommended", "helpers:pinGitHubActionDigests"],
|
|
4
|
+
"dependencyDashboard": true,
|
|
5
|
+
"minimumReleaseAge": "1 day",
|
|
6
|
+
"prConcurrentLimit": 5,
|
|
7
|
+
"prHourlyLimit": 2,
|
|
8
|
+
"rangeStrategy": "bump",
|
|
9
|
+
"schedule": ["before 5am on monday"],
|
|
10
|
+
"timezone": "Etc/UTC",
|
|
11
|
+
"packageRules": [
|
|
12
|
+
{
|
|
13
|
+
"matchManagers": ["github-actions"],
|
|
14
|
+
"groupName": "github-actions",
|
|
15
|
+
"labels": ["dependencies", "github-actions", "security"]
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"matchManagers": ["npm"],
|
|
19
|
+
"matchUpdateTypes": ["patch", "minor"],
|
|
20
|
+
"groupName": "npm minor and patch updates",
|
|
21
|
+
"labels": ["dependencies", "npm"]
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"matchUpdateTypes": ["major"],
|
|
25
|
+
"dependencyDashboardApproval": true,
|
|
26
|
+
"labels": ["dependencies", "major"]
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: Ultramodern Workspace Gates
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
defaults:
|
|
11
|
+
run:
|
|
12
|
+
shell: bash
|
|
13
|
+
|
|
14
|
+
env:
|
|
15
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
16
|
+
|
|
17
|
+
concurrency:
|
|
18
|
+
group: ultramodern-workspace-gates-${{ github.workflow }}-${{ github.ref }}
|
|
19
|
+
cancel-in-progress: true
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
ultramodern-workspace-gates:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
timeout-minutes: 30
|
|
25
|
+
steps:
|
|
26
|
+
- name: Harden Runner
|
|
27
|
+
uses: step-security/harden-runner@ab7a9404c0f3da075243ca237b5fac12c98deaa5 # v2
|
|
28
|
+
with:
|
|
29
|
+
egress-policy: audit
|
|
30
|
+
|
|
31
|
+
- name: Checkout
|
|
32
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
33
|
+
with:
|
|
34
|
+
fetch-depth: 1
|
|
35
|
+
persist-credentials: false
|
|
36
|
+
|
|
37
|
+
- name: Setup Node.js
|
|
38
|
+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
39
|
+
with:
|
|
40
|
+
node-version: 24
|
|
41
|
+
|
|
42
|
+
- name: Setup mise
|
|
43
|
+
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
|
44
|
+
|
|
45
|
+
- name: Install Dependencies
|
|
46
|
+
run: mise exec -- pnpm install --frozen-lockfile
|
|
47
|
+
|
|
48
|
+
- name: Validate Ultramodern Workspace Contract
|
|
49
|
+
run: mise exec -- pnpm run ultramodern:check
|
|
50
|
+
|
|
51
|
+
- name: Build Workspace Apps
|
|
52
|
+
env:
|
|
53
|
+
MODERN_PUBLIC_SITE_URL: http://localhost:8080
|
|
54
|
+
run: mise exec -- pnpm build
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# UltraModern Agent Contract
|
|
2
2
|
|
|
3
|
-
This workspace is generated as an agent-ready UltraModern.js SuperApp
|
|
3
|
+
This workspace is generated as an agent-ready UltraModern.js SuperApp shell.
|
|
4
|
+
Agents should treat the files under `.agents/skills` as local project
|
|
5
|
+
instructions, not optional reading.
|
|
4
6
|
|
|
5
7
|
## Quality Gates
|
|
6
8
|
|
|
@@ -8,6 +10,19 @@ This workspace is generated as an agent-ready UltraModern.js SuperApp. Agents sh
|
|
|
8
10
|
- `pnpm format` runs oxfmt.
|
|
9
11
|
- `pnpm typecheck` runs effect-tsgo as the TypeScript checker.
|
|
10
12
|
- `pnpm check` runs formatting, linting, effect-tsgo, private-skill availability checks, and the generated workspace contract.
|
|
13
|
+
- Generated Codex stop hooks and subagent-stop hooks run `pnpm format && pnpm lint:fix && pnpm check`.
|
|
14
|
+
- `postinstall` formats the generated tree, installs agent skills and reference repos, then installs `lefthook` when the workspace is inside a Git worktree. Generated `lefthook.yml` runs `pnpm format && pnpm lint:fix && pnpm check` on pre-commit; pre-push runs `pnpm check`.
|
|
15
|
+
|
|
16
|
+
## Localized Routes
|
|
17
|
+
|
|
18
|
+
Generated apps keep locale-prefixed entry routes under `src/routes/[lang]`,
|
|
19
|
+
static language links, and canonical plus `hreflang` metadata. A new workspace
|
|
20
|
+
starts shell-only; `create <domain> --vertical` adds route-owned metadata,
|
|
21
|
+
localized resources, and Effect BFF surfaces for that domain. Runtime i18n is
|
|
22
|
+
not enabled in the starter because the current React 19 + Module Federation
|
|
23
|
+
streaming SSR stack must render predictably first. Production builds fail unless
|
|
24
|
+
`MODERN_PUBLIC_SITE_URL` is set per deployed app, so canonical URLs always use
|
|
25
|
+
the production origin.
|
|
11
26
|
|
|
12
27
|
## Required Skill Baseline
|
|
13
28
|
|
|
@@ -20,6 +35,9 @@ Use these skills when the task touches the matching subsystem:
|
|
|
20
35
|
- `rslib-best-practices`: Shared packages, generated libraries, declaration output, and Rslib configuration.
|
|
21
36
|
- `rslib-modern-package`: Package contracts for shared libraries, exports, side effects, dependency placement, README, and release readiness.
|
|
22
37
|
- `rstest-best-practices`: Rstest configuration, test writing, mocking, snapshots, coverage, and CI test behavior.
|
|
38
|
+
- `mf`: Module Federation docs, Modern.js integration, DTS/type checks, shared dependency checks, runtime errors, and observability troubleshooting.
|
|
39
|
+
|
|
40
|
+
The public `module-federation/agent-skills` repository is installed during `pnpm install` and `pnpm skills:install`. `pnpm skills:check` fails when the required public `mf` skill is missing.
|
|
23
41
|
|
|
24
42
|
## Private Skills
|
|
25
43
|
|
|
@@ -29,17 +47,30 @@ ScriptedAlchemy/TechsioCZ skills are private and are cloned only when the curren
|
|
|
29
47
|
pnpm skills:install
|
|
30
48
|
```
|
|
31
49
|
|
|
32
|
-
The installer copies only the
|
|
50
|
+
The installer copies only the pinned private skills from `.agents/skills-lock.json`: `plan-graph`, `dag`, `subagent-graph`, `helm`, and `debugger-mode`.
|
|
51
|
+
|
|
52
|
+
## Agent Reference Repositories
|
|
53
|
+
|
|
54
|
+
The workspace installs read-only source references under `repos/` by default during `pnpm install` using `git subtree add --squash`. These repositories are reference material for coding agents, not application source:
|
|
55
|
+
|
|
56
|
+
- `repos/effect` from `Effect-TS/effect`.
|
|
57
|
+
- `repos/ultramodern.js` from `BleedingDev/ultramodern.js`.
|
|
58
|
+
|
|
59
|
+
Agents may read files under `repos/` to understand upstream patterns, APIs, and project conventions. Do not edit files under `repos/`, import from them, or make production code depend on them. To skip this setup, run installs with `ULTRAMODERN_SKIP_AGENT_REPOS=1`.
|
|
33
60
|
|
|
34
61
|
## Project Priorities
|
|
35
62
|
|
|
36
63
|
- Keep `presetUltramodern` as the single preset.
|
|
37
|
-
-
|
|
64
|
+
- Keep the initial workspace shell-only unless a user explicitly asks for a
|
|
65
|
+
starter vertical.
|
|
66
|
+
- Use `create <domain> --vertical` as the growth path for real business
|
|
67
|
+
MicroVerticals.
|
|
68
|
+
- Prefer Effect for BFF code.
|
|
38
69
|
- Prefer TanStack Router for app routing.
|
|
39
|
-
- Keep design-system code as
|
|
70
|
+
- Keep UI-kit or design-system code as ordinary vertical or shared package code, not a special core path.
|
|
40
71
|
- Keep generated packages explicit and publishable: stable `exports`, correct declarations, small public APIs, and clear ownership metadata.
|
|
41
72
|
- Do not add migration tooling or codemods unless the project owner explicitly asks for migration work.
|
|
42
73
|
|
|
43
74
|
## Skill Provenance
|
|
44
75
|
|
|
45
|
-
The vendored Rstack skills and private TechsioCZ skill
|
|
76
|
+
The vendored Rstack skills, public Module Federation skill, and private TechsioCZ skill set are pinned in `.agents/skills-lock.json`. Do not update, remove, or replace them casually. If a skill needs updating, update the lock file and run `pnpm check`.
|
|
@@ -3,23 +3,46 @@
|
|
|
3
3
|
Generated UltraModern SuperApp workspace.
|
|
4
4
|
|
|
5
5
|
This workspace keeps `presetUltramodern(...)` as the single public
|
|
6
|
-
UltraModern.js 3.0 SuperApp surface and
|
|
7
|
-
starter topology:
|
|
6
|
+
UltraModern.js 3.0 SuperApp surface and starts with an explicit shell:
|
|
8
7
|
|
|
9
|
-
- `apps/shell-super-app` owns shell route assembly
|
|
10
|
-
|
|
11
|
-
- `
|
|
12
|
-
|
|
13
|
-
- `
|
|
14
|
-
- `packages/shared-*` provide placeholders for cross-workspace contracts.
|
|
8
|
+
- `apps/shell-super-app` owns shell route assembly, Module Federation host
|
|
9
|
+
wiring, shared SSR/i18n runtime setup, and the boundary debugger.
|
|
10
|
+
- `packages/shared-*` provide placeholders for cross-workspace contracts,
|
|
11
|
+
design tokens, and Effect API sharing.
|
|
12
|
+
- `verticals/*` is intentionally empty until a real business domain is added.
|
|
15
13
|
|
|
16
|
-
|
|
14
|
+
Add a full-stack MicroVertical when the product needs one:
|
|
17
15
|
|
|
18
16
|
```bash
|
|
17
|
+
pnpm dlx @bleedingdev/modern-js-create transportation --vertical
|
|
18
|
+
pnpm dlx @bleedingdev/modern-js-create payments --vertical
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Each added vertical owns its UI/routes, browser-safe Module Federation exposes,
|
|
22
|
+
localized route metadata, CSS prefix, Effect BFF handlers, local API contract,
|
|
23
|
+
and typed client surface. Server handlers and Effect client/contract modules
|
|
24
|
+
stay out of browser exposes.
|
|
25
|
+
|
|
26
|
+
Run the scaffold validator before adding business code and after every
|
|
27
|
+
`--vertical` mutation:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
mise install
|
|
19
31
|
pnpm ultramodern:check
|
|
20
32
|
```
|
|
21
33
|
|
|
22
|
-
|
|
34
|
+
By default, `pnpm install` also prepares read-only agent reference repositories
|
|
35
|
+
under `repos/` for Effect and UltraModern.js source lookup using squashed git
|
|
36
|
+
subtrees. Disable this setup with
|
|
37
|
+
`ULTRAMODERN_SKIP_AGENT_REPOS=1 pnpm install`, or rerun it
|
|
38
|
+
explicitly with `pnpm agents:refs:install`.
|
|
39
|
+
|
|
40
|
+
The topology and ownership metadata are generated under `topology/`. The
|
|
41
|
+
workspace also ships `.github/workflows/ultramodern-workspace-gates.yml` and
|
|
42
|
+
`.github/renovate.json` with read-only workflow permissions, commit-pinned
|
|
43
|
+
actions, frozen installs, StepSecurity audit-mode runner hardening, dependency
|
|
44
|
+
dashboard review, one-day release age, grouped updates, and manual approval for
|
|
45
|
+
major upgrades.
|
|
23
46
|
|
|
24
47
|
Package source metadata is generated at
|
|
25
48
|
`.modernjs/ultramodern-package-source.json`. The default strategy keeps
|