@bleedingdev/modern-js-create 3.2.0-ultramodern.6 → 3.2.0-ultramodern.61
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 +116 -22
- package/dist/index.js +4563 -603
- 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/.agents/skills-lock.json +34 -0
- 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 +23 -0
- package/template/README.md +60 -34
- package/template/api/effect/index.ts.handlebars +7 -45
- package/template/config/public/locales/cs/translation.json +39 -0
- package/template/config/public/locales/en/translation.json +39 -0
- package/template/lefthook.yml +15 -0
- package/template/modern.config.ts.handlebars +44 -23
- package/template/oxfmt.config.ts +8 -0
- package/template/oxlint.config.ts +12 -0
- package/template/package.json.handlebars +50 -31
- package/template/pnpm-workspace.yaml +19 -0
- package/template/rstest.config.mts +7 -0
- package/template/scripts/bootstrap-agent-skills.mjs +135 -0
- package/template/scripts/check-i18n-strings.mjs +83 -0
- package/template/scripts/validate-ultramodern.mjs.handlebars +439 -17
- 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/tsconfig.json +106 -2
- package/template-workspace/.agents/agent-reference-repos.json +24 -0
- package/template-workspace/.agents/rstackjs-agent-skills-LICENSE +21 -0
- package/template-workspace/.agents/skills/rsbuild-best-practices/SKILL.md +57 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/SKILL.md +96 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/command-map.md +113 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/common-analysis-patterns.md +190 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor-common.md +88 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor-rspack.md +138 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor-webpack.md +71 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor.md +39 -0
- package/template-workspace/.agents/skills/rsdoctor-analysis/references/rsdoctor-data-types.md +103 -0
- package/template-workspace/.agents/skills/rslib-best-practices/SKILL.md +58 -0
- package/template-workspace/.agents/skills/rslib-modern-package/SKILL.md +173 -0
- package/template-workspace/.agents/skills/rspack-best-practices/SKILL.md +70 -0
- package/template-workspace/.agents/skills/rspack-tracing/SKILL.md +75 -0
- package/template-workspace/.agents/skills/rspack-tracing/references/bottlenecks.md +47 -0
- package/template-workspace/.agents/skills/rspack-tracing/references/tracing-guide.md +38 -0
- package/template-workspace/.agents/skills/rspack-tracing/scripts/analyze_trace.js +184 -0
- package/template-workspace/.agents/skills/rstest-best-practices/SKILL.md +133 -0
- package/template-workspace/.agents/skills-lock.json +114 -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 +76 -0
- package/template-workspace/README.md.handlebars +33 -10
- package/template-workspace/lefthook.yml +15 -0
- package/template-workspace/oxfmt.config.ts +16 -0
- package/template-workspace/oxlint.config.ts +19 -0
- package/template-workspace/pnpm-workspace.yaml +20 -10
- package/template-workspace/scripts/bootstrap-agent-skills.mjs +163 -0
- package/template-workspace/scripts/setup-agent-reference-repos.mjs +368 -0
- package/template/biome.json +0 -41
- package/template/src/routes/page.tsx.handlebars +0 -119
- package/template-workspace/scripts/validate-ultramodern-workspace.mjs.handlebars +0 -276
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
// Parse duration string (e.g., "1.23ms", "456.78µs", "0.12s") to milliseconds
|
|
7
|
+
function parseDuration(durationStr) {
|
|
8
|
+
if (!durationStr) return 0;
|
|
9
|
+
|
|
10
|
+
const match = durationStr.match(/^([\d.]+)(ms|µs|s|ns)$/);
|
|
11
|
+
if (!match) return 0;
|
|
12
|
+
|
|
13
|
+
const value = parseFloat(match[1]);
|
|
14
|
+
const unit = match[2];
|
|
15
|
+
|
|
16
|
+
switch (unit) {
|
|
17
|
+
case 's':
|
|
18
|
+
return value * 1000;
|
|
19
|
+
case 'ms':
|
|
20
|
+
return value;
|
|
21
|
+
case 'µs':
|
|
22
|
+
return value / 1000;
|
|
23
|
+
case 'ns':
|
|
24
|
+
return value / 1000000;
|
|
25
|
+
default:
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Get trace file path
|
|
31
|
+
const tracePath = process.argv[2] || path.join(__dirname, 'trace.json');
|
|
32
|
+
|
|
33
|
+
if (!fs.existsSync(tracePath)) {
|
|
34
|
+
console.error(`Error: Trace file not found at ${tracePath}`);
|
|
35
|
+
console.error('Usage: node analyze_trace.js <path-to-trace.json>');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log(`Analyzing trace file: ${tracePath}\n`);
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const fileContent = fs.readFileSync(tracePath, 'utf8');
|
|
43
|
+
|
|
44
|
+
// Parse line-delimited JSON
|
|
45
|
+
const events = fileContent
|
|
46
|
+
.trim()
|
|
47
|
+
.split('\n')
|
|
48
|
+
.map(line => {
|
|
49
|
+
try {
|
|
50
|
+
return JSON.parse(line);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
.filter(Boolean);
|
|
56
|
+
|
|
57
|
+
if (!events.length) {
|
|
58
|
+
console.error('No valid trace events found.');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
console.log('=== Rspack Build Performance Analysis ===\n');
|
|
63
|
+
console.log(`Total events: ${events.length}\n`);
|
|
64
|
+
|
|
65
|
+
// Categorize events by target
|
|
66
|
+
const pluginStats = new Map();
|
|
67
|
+
const loaderStats = new Map();
|
|
68
|
+
|
|
69
|
+
events.forEach(event => {
|
|
70
|
+
const target = event.target;
|
|
71
|
+
const timeField = event.fields?.['time.busy'];
|
|
72
|
+
|
|
73
|
+
if (!timeField) return;
|
|
74
|
+
|
|
75
|
+
const duration = parseDuration(timeField);
|
|
76
|
+
|
|
77
|
+
if (target === 'Plugin Analysis') {
|
|
78
|
+
// Plugin performance
|
|
79
|
+
const pluginName = event.span?.name;
|
|
80
|
+
if (!pluginName) return;
|
|
81
|
+
|
|
82
|
+
if (!pluginStats.has(pluginName)) {
|
|
83
|
+
pluginStats.set(pluginName, {
|
|
84
|
+
count: 0,
|
|
85
|
+
total: 0,
|
|
86
|
+
max: 0,
|
|
87
|
+
min: Infinity,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const stat = pluginStats.get(pluginName);
|
|
92
|
+
stat.count++;
|
|
93
|
+
stat.total += duration;
|
|
94
|
+
stat.max = Math.max(stat.max, duration);
|
|
95
|
+
stat.min = Math.min(stat.min, duration);
|
|
96
|
+
} else if (target === 'Loader Analysis') {
|
|
97
|
+
// Loader performance
|
|
98
|
+
let loaderName = event.span?.name;
|
|
99
|
+
|
|
100
|
+
// For pitch phase (span.name is null), use resource path
|
|
101
|
+
if (!loaderName) {
|
|
102
|
+
const resource = event.fields?.resource;
|
|
103
|
+
if (resource) {
|
|
104
|
+
// Extract filename from resource path
|
|
105
|
+
const cleanResource = resource.replace(/^"|"$/g, ''); // Remove quotes
|
|
106
|
+
const filename = cleanResource.split('/').pop();
|
|
107
|
+
loaderName = `Loader pitch for ${filename}`;
|
|
108
|
+
} else {
|
|
109
|
+
loaderName = 'Loader pitch (unknown resource)';
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!loaderStats.has(loaderName)) {
|
|
114
|
+
loaderStats.set(loaderName, {
|
|
115
|
+
count: 0,
|
|
116
|
+
total: 0,
|
|
117
|
+
max: 0,
|
|
118
|
+
min: Infinity,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const stat = loaderStats.get(loaderName);
|
|
123
|
+
stat.count++;
|
|
124
|
+
stat.total += duration;
|
|
125
|
+
stat.max = Math.max(stat.max, duration);
|
|
126
|
+
stat.min = Math.min(stat.min, duration);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Display Plugin Analysis
|
|
131
|
+
if (pluginStats.size > 0) {
|
|
132
|
+
console.log('🔌 Plugin Analysis (by name):');
|
|
133
|
+
console.log('─'.repeat(80));
|
|
134
|
+
|
|
135
|
+
const sortedPlugins = [...pluginStats.entries()].sort(
|
|
136
|
+
(a, b) => b[1].total - a[1].total,
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
sortedPlugins.forEach(([name, stat]) => {
|
|
140
|
+
const avg = stat.total / stat.count;
|
|
141
|
+
console.log(`${name}`);
|
|
142
|
+
console.log(
|
|
143
|
+
` Total: ${stat.total.toFixed(2)}ms | Count: ${stat.count} | ` +
|
|
144
|
+
`Avg: ${avg.toFixed(2)}ms | Max: ${stat.max.toFixed(2)}ms | Min: ${stat.min.toFixed(2)}ms`,
|
|
145
|
+
);
|
|
146
|
+
console.log('');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const totalPluginTime = [...pluginStats.values()].reduce(
|
|
150
|
+
(sum, stat) => sum + stat.total,
|
|
151
|
+
0,
|
|
152
|
+
);
|
|
153
|
+
console.log(`Total Plugin Time: ${totalPluginTime.toFixed(2)}ms\n`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Display Loader Analysis
|
|
157
|
+
if (loaderStats.size > 0) {
|
|
158
|
+
console.log('\n🔧 Loader Analysis (by name):');
|
|
159
|
+
console.log('─'.repeat(80));
|
|
160
|
+
|
|
161
|
+
const sortedLoaders = [...loaderStats.entries()].sort(
|
|
162
|
+
(a, b) => b[1].total - a[1].total,
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
sortedLoaders.forEach(([name, stat]) => {
|
|
166
|
+
const avg = stat.total / stat.count;
|
|
167
|
+
console.log(`${name}`);
|
|
168
|
+
console.log(
|
|
169
|
+
` Total: ${stat.total.toFixed(2)}ms | Count: ${stat.count} | ` +
|
|
170
|
+
`Avg: ${avg.toFixed(2)}ms | Max: ${stat.max.toFixed(2)}ms | Min: ${stat.min.toFixed(2)}ms`,
|
|
171
|
+
);
|
|
172
|
+
console.log('');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const totalLoaderTime = [...loaderStats.values()].reduce(
|
|
176
|
+
(sum, stat) => sum + stat.total,
|
|
177
|
+
0,
|
|
178
|
+
);
|
|
179
|
+
console.log(`Total Loader Time: ${totalLoaderTime.toFixed(2)}ms\n`);
|
|
180
|
+
}
|
|
181
|
+
} catch (err) {
|
|
182
|
+
console.error('Error processing trace file:', err);
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rstest-best-practices
|
|
3
|
+
description: Rstest best practices for config, CLI workflow, test writing, mocking, snapshot testing, DOM testing, coverage, multi-project setup, CI integration, performance and debugging. Use when writing, reviewing, or troubleshooting Rstest test projects.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Rstest Best Practices
|
|
7
|
+
|
|
8
|
+
Apply these rules when writing or reviewing Rstest test projects.
|
|
9
|
+
|
|
10
|
+
## Configuration
|
|
11
|
+
|
|
12
|
+
- Use `rstest.config.ts` and `defineConfig` from `@rstest/core`
|
|
13
|
+
- Prefer explicit imports `import { test, expect, describe } from '@rstest/core'` over `globals: true`
|
|
14
|
+
- For Rsbuild projects, use `@rstest/adapter-rsbuild` with `extends: withRsbuildConfig()` to reuse build config
|
|
15
|
+
- For Rslib projects, use `@rstest/adapter-rslib` with `extends: withRslibConfig()` to reuse build config
|
|
16
|
+
- Use `setupFiles` for shared test setup (e.g., custom matchers, cleanup hooks)
|
|
17
|
+
- When using Rsbuild plugins (e.g., `@rsbuild/plugin-react`), add them via the `plugins` field
|
|
18
|
+
- For deep-level or advanced build configuration needs, use `tools.rspack` or `tools.bundlerChain`
|
|
19
|
+
|
|
20
|
+
## CLI
|
|
21
|
+
|
|
22
|
+
- Use `rstest` or `rstest run` to run tests (`run` disables watch mode, suitable for CI)
|
|
23
|
+
- Use `rstest --watch` or `rstest watch` for local development with file watching
|
|
24
|
+
- Use `rstest list` to list all test files and test names
|
|
25
|
+
- Use `rstest -u` to update snapshots
|
|
26
|
+
- Use `--reporter=verbose` when debugging test failures for detailed output
|
|
27
|
+
- Use `--config` (`-c`) to specify a custom config file path
|
|
28
|
+
|
|
29
|
+
## Test writing
|
|
30
|
+
|
|
31
|
+
- Import test APIs from `@rstest/core`: `test`, `describe`, `expect`, `beforeEach`, `afterEach`, etc.
|
|
32
|
+
- Use `test` or `it` for test cases; use `describe` for grouping related tests
|
|
33
|
+
- Use `.only` to focus on specific tests during development, but never commit `.only` to the codebase
|
|
34
|
+
- Use `.skip` or `.todo` to mark incomplete or temporarily skipped tests
|
|
35
|
+
- Prefer small, focused test cases that test a single behavior
|
|
36
|
+
- For async error paths, prefer `await expect(fn()).rejects.toThrow(ErrorClass)` (or `.rejects.toMatchObject({ ... })`) over `try/catch` with `expect.fail` or `.catch(e => e)` patterns — the matcher form fails clearly if the promise unexpectedly resolves, keeps the assertion in one chain, and avoids forgetting to assert the throw at all
|
|
37
|
+
- For async happy paths, use `await expect(fn()).resolves.toEqual(...)` for the same reason
|
|
38
|
+
- Use `includeSource` for in-source testing of small utility functions (Rust-style `import.meta.rstest`)
|
|
39
|
+
- For in-source tests, wrap test code in `if (import.meta.rstest) { ... }` and define `import.meta.rstest` as `false` in production build config
|
|
40
|
+
|
|
41
|
+
## Test environment
|
|
42
|
+
|
|
43
|
+
- Use `testEnvironment: 'node'` (default) for Node.js / server-side code
|
|
44
|
+
- Use `testEnvironment: 'jsdom'` or `testEnvironment: 'happy-dom'` for DOM / browser API testing
|
|
45
|
+
- Install `jsdom` or `happy-dom` as a dev dependency when using DOM environments
|
|
46
|
+
- Prefer `happy-dom` for faster DOM testing; use `jsdom` when better browser API compatibility is needed
|
|
47
|
+
- For real browser testing, use `@rstest/browser` with Playwright
|
|
48
|
+
- Use inline project configs to run different test environments within one project (e.g., `node` and `jsdom` projects)
|
|
49
|
+
|
|
50
|
+
## React / Vue testing
|
|
51
|
+
|
|
52
|
+
- For React: use `@rsbuild/plugin-react` plugin and `@testing-library/react` for component testing
|
|
53
|
+
- For Vue: use `@rsbuild/plugin-vue` plugin and `@testing-library/vue` for component testing
|
|
54
|
+
- Create a `rstest.setup.ts` with `expect.extend(jestDomMatchers)` and `afterEach(() => cleanup())` for Testing Library
|
|
55
|
+
- Add the setup file to `setupFiles` in config
|
|
56
|
+
- For SSR testing, use `testEnvironment: 'node'` and test with `react-dom/server` or framework-specific SSR APIs
|
|
57
|
+
|
|
58
|
+
## Mocking
|
|
59
|
+
|
|
60
|
+
- Use `rs.mock('./module')` to mock modules
|
|
61
|
+
- Use `rs.fn()` to create mock functions
|
|
62
|
+
- Use `rs.spyOn(object, 'method')` to spy on methods
|
|
63
|
+
- Prefer `clearMocks`, `resetMocks`, or `restoreMocks` config options to automatically clean up mocks between tests
|
|
64
|
+
- Use factory functions in `rs.mock('./module', () => ({ ... }))` to provide mock implementations
|
|
65
|
+
|
|
66
|
+
## Snapshot testing
|
|
67
|
+
|
|
68
|
+
- Use `toMatchSnapshot()` for general snapshot testing
|
|
69
|
+
- Use `toMatchInlineSnapshot()` for small, readable inline snapshots
|
|
70
|
+
- Use `toMatchFileSnapshot()` for large or structured outputs (e.g., HTML, generated code)
|
|
71
|
+
- Keep snapshots concise — only include relevant data, avoid timestamps and session IDs
|
|
72
|
+
- Use `expect.addSnapshotSerializer()` to mask paths or sensitive data in snapshots
|
|
73
|
+
- Use `path-serializer` to normalize file paths across platforms
|
|
74
|
+
- Review snapshot changes carefully in code review
|
|
75
|
+
|
|
76
|
+
## Coverage
|
|
77
|
+
|
|
78
|
+
- Enable coverage with `--coverage` CLI flag or `coverage.enabled: true` in config
|
|
79
|
+
- Install `@rstest/coverage-istanbul` for the Istanbul coverage provider
|
|
80
|
+
- Use `coverage.include` to specify source files for coverage (e.g., `['src/**/*.{js,ts,tsx}']`)
|
|
81
|
+
- Use `coverage.thresholds` to enforce minimum coverage requirements
|
|
82
|
+
- Use `coverage.reporters` to generate reports in different formats (e.g., `text`, `lcov`, `html`)
|
|
83
|
+
|
|
84
|
+
## Multi-project testing
|
|
85
|
+
|
|
86
|
+
- Use `projects` field in root config to define multiple test projects
|
|
87
|
+
- For monorepos, use glob patterns like `'packages/*'` to auto-discover sub-projects
|
|
88
|
+
- Use `defineProject` helper in sub-project configs
|
|
89
|
+
- Extract shared config and use `mergeRstestConfig` to compose project configs
|
|
90
|
+
- Global options (`reporters`, `pool`, `isolate`, `coverage`, `bail`) must be set at the root level, not in projects
|
|
91
|
+
|
|
92
|
+
## CI integration
|
|
93
|
+
|
|
94
|
+
- Use `rstest run` (not `rstest watch`) in CI
|
|
95
|
+
- Use `--shard` for parallel test execution across CI machines (e.g., `--shard 1/3`)
|
|
96
|
+
- Use `--reporter=blob` with `rstest merge-reports` to combine sharded results
|
|
97
|
+
- Use `--reporter=junit` with `outputPath` for CI report integration
|
|
98
|
+
- The `github-actions` reporter is auto-enabled in GitHub Actions for inline error annotations
|
|
99
|
+
- Use `--bail` to stop early on first failure when appropriate
|
|
100
|
+
|
|
101
|
+
## Performance
|
|
102
|
+
|
|
103
|
+
- Disable `isolate` (`--no-isolate`) when tests have no side effects for faster execution via module cache reuse
|
|
104
|
+
- Use `pool.maxWorkers` to control parallelism based on available resources
|
|
105
|
+
- Keep test build fast by avoiding unnecessary Rspack plugins in test config
|
|
106
|
+
- Use test filtering (`rstest <pattern>` or `-t <name>`) to run only relevant tests during development
|
|
107
|
+
- Leverage watch mode's incremental re-runs for fast local feedback
|
|
108
|
+
|
|
109
|
+
## Debugging
|
|
110
|
+
|
|
111
|
+
- Run with `DEBUG=rstest` to enable debug mode, which writes final configs and build outputs to disk
|
|
112
|
+
- Read generated files in `dist/.rstest-temp/.rsbuild/` to confirm final Rstest/Rsbuild/Rspack config
|
|
113
|
+
- Use VS Code's JavaScript Debug Terminal to run `rstest` with breakpoints
|
|
114
|
+
- Use `--reporter=verbose` for detailed per-test output
|
|
115
|
+
- Use `--printConsoleTrace` to trace console calls to their source
|
|
116
|
+
- Add VS Code launch config for debugging specific test files with `@rstest/core/bin/rstest.js`
|
|
117
|
+
|
|
118
|
+
## Profiling
|
|
119
|
+
|
|
120
|
+
- Use Rsdoctor with `RSDOCTOR=true rstest run` to analyze test build performance
|
|
121
|
+
- Use `samply` for native profiling of both main and worker processes
|
|
122
|
+
- Use Node.js `--heap-prof` for memory profiling
|
|
123
|
+
|
|
124
|
+
## Toolchain integration
|
|
125
|
+
|
|
126
|
+
- Use the official VS Code extension (`rstack.rstest`) for in-editor test running and debugging
|
|
127
|
+
- For Rslib libraries, use `@rstest/adapter-rslib` for config reuse
|
|
128
|
+
- For Rsbuild apps, use `@rstest/adapter-rsbuild` for config reuse
|
|
129
|
+
- Use `process.env.RSTEST` to detect test environment and apply test-specific config
|
|
130
|
+
|
|
131
|
+
## Documentation
|
|
132
|
+
|
|
133
|
+
- For the latest Rstest docs, read https://rstest.rs/llms.txt
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"source": {
|
|
4
|
+
"repository": "https://github.com/rstackjs/agent-skills",
|
|
5
|
+
"commit": "61c948b42512e223bad44b83af4080eba48b2677",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"licensePath": ".agents/rstackjs-agent-skills-LICENSE"
|
|
8
|
+
},
|
|
9
|
+
"installDir": ".agents/skills",
|
|
10
|
+
"sources": [
|
|
11
|
+
{
|
|
12
|
+
"id": "rstack-agent-skills",
|
|
13
|
+
"visibility": "public",
|
|
14
|
+
"repository": "https://github.com/rstackjs/agent-skills",
|
|
15
|
+
"commit": "61c948b42512e223bad44b83af4080eba48b2677",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"licensePath": ".agents/rstackjs-agent-skills-LICENSE",
|
|
18
|
+
"install": "vendored"
|
|
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
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "techsiocz-private",
|
|
36
|
+
"visibility": "private",
|
|
37
|
+
"repository": "https://github.com/TechsioCZ/skills",
|
|
38
|
+
"install": "clone-if-authorized",
|
|
39
|
+
"baseline": [
|
|
40
|
+
{
|
|
41
|
+
"name": "plan-graph",
|
|
42
|
+
"reason": "Build and validate DAGs from .plan.md files"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "dag",
|
|
46
|
+
"reason": "Inspect current plan frontiers and blocked lanes"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "subagent-graph",
|
|
50
|
+
"reason": "Design dependency-aware multi-agent launch graphs"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "helm",
|
|
54
|
+
"reason": "Steer already-running multi-agent work"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "debugger-mode",
|
|
58
|
+
"reason": "Run hypothesis-driven debugging with runtime evidence"
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
"baseline": [
|
|
64
|
+
{
|
|
65
|
+
"name": "rsbuild-best-practices",
|
|
66
|
+
"path": ".agents/skills/rsbuild-best-practices",
|
|
67
|
+
"reason": "Modern.js application build configuration and Rsbuild troubleshooting"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"name": "rspack-best-practices",
|
|
71
|
+
"path": ".agents/skills/rspack-best-practices",
|
|
72
|
+
"reason": "Rspack bundling, CSS, asset, profiling, and production behavior"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"name": "rspack-tracing",
|
|
76
|
+
"path": ".agents/skills/rspack-tracing",
|
|
77
|
+
"reason": "Trace-backed Rspack failure and performance debugging"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"name": "rsdoctor-analysis",
|
|
81
|
+
"path": ".agents/skills/rsdoctor-analysis",
|
|
82
|
+
"reason": "Evidence-backed bundle composition, duplication, and retained-module analysis"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"name": "rslib-best-practices",
|
|
86
|
+
"path": ".agents/skills/rslib-best-practices",
|
|
87
|
+
"reason": "Rslib shared package and design-system library authoring"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"name": "rslib-modern-package",
|
|
91
|
+
"path": ".agents/skills/rslib-modern-package",
|
|
92
|
+
"reason": "Modern package contracts, exports, declarations, side effects, and release readiness"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"name": "rstest-best-practices",
|
|
96
|
+
"path": ".agents/skills/rstest-best-practices",
|
|
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"
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
"excludedByDefault": [
|
|
106
|
+
"migrate-to-rsbuild",
|
|
107
|
+
"migrate-to-rslib",
|
|
108
|
+
"migrate-to-rslint",
|
|
109
|
+
"migrate-to-rstest",
|
|
110
|
+
"rsbuild-v2-upgrade",
|
|
111
|
+
"rspack-v2-upgrade",
|
|
112
|
+
"rspress-v2-upgrade"
|
|
113
|
+
]
|
|
114
|
+
}
|
|
@@ -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
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# UltraModern Agent Contract
|
|
2
|
+
|
|
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.
|
|
6
|
+
|
|
7
|
+
## Quality Gates
|
|
8
|
+
|
|
9
|
+
- `pnpm lint` runs Oxlint with the Ultracite preset.
|
|
10
|
+
- `pnpm format` runs oxfmt.
|
|
11
|
+
- `pnpm typecheck` runs effect-tsgo as the TypeScript checker.
|
|
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` installs `lefthook` when the workspace is inside a Git worktree. Generated `lefthook.yml` runs `pnpm format`, `pnpm lint:fix`, and `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.
|
|
26
|
+
|
|
27
|
+
## Required Skill Baseline
|
|
28
|
+
|
|
29
|
+
Use these skills when the task touches the matching subsystem:
|
|
30
|
+
|
|
31
|
+
- `rsbuild-best-practices`: Modern.js app build configuration, Rsbuild options, assets, type checking, and build debugging.
|
|
32
|
+
- `rspack-best-practices`: Rspack-level bundling, CSS, assets, profiling, and production build behavior.
|
|
33
|
+
- `rspack-tracing`: Rspack build failures, slow builds, crash localization, and trace analysis.
|
|
34
|
+
- `rsdoctor-analysis`: Evidence-based bundle analysis from `rsdoctor-data.json`, including duplicate packages, large chunks, and retained modules.
|
|
35
|
+
- `rslib-best-practices`: Shared packages, generated libraries, declaration output, and Rslib configuration.
|
|
36
|
+
- `rslib-modern-package`: Package contracts for shared libraries, exports, side effects, dependency placement, README, and release readiness.
|
|
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.
|
|
41
|
+
|
|
42
|
+
## Private Skills
|
|
43
|
+
|
|
44
|
+
ScriptedAlchemy/TechsioCZ skills are private and are cloned only when the current developer is authorized for `TechsioCZ/skills`.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pnpm skills:install
|
|
48
|
+
```
|
|
49
|
+
|
|
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`.
|
|
60
|
+
|
|
61
|
+
## Project Priorities
|
|
62
|
+
|
|
63
|
+
- Keep `presetUltramodern` as the single preset.
|
|
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.
|
|
69
|
+
- Prefer TanStack Router for app routing.
|
|
70
|
+
- Keep UI-kit or design-system code as ordinary vertical or shared package code, not a special core path.
|
|
71
|
+
- Keep generated packages explicit and publishable: stable `exports`, correct declarations, small public APIs, and clear ownership metadata.
|
|
72
|
+
- Do not add migration tooling or codemods unless the project owner explicitly asks for migration work.
|
|
73
|
+
|
|
74
|
+
## Skill Provenance
|
|
75
|
+
|
|
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 create modern@latest transportation --vertical
|
|
18
|
+
pnpm create modern@latest 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
|