@bleedingdev/modern-js-create 3.2.0-ultramodern.2 → 3.2.0-ultramodern.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +12 -0
  2. package/dist/index.js +676 -198
  3. package/package.json +5 -2
  4. package/template/.agents/skills-lock.json +34 -0
  5. package/template/.github/renovate.json +53 -0
  6. package/template/.github/workflows/ultramodern-gates.yml.handlebars +26 -4
  7. package/template/AGENTS.md +27 -0
  8. package/template/README.md +7 -3
  9. package/template/api/effect/index.ts.handlebars +7 -45
  10. package/template/config/public/locales/cs/translation.json +39 -0
  11. package/template/config/public/locales/en/translation.json +39 -0
  12. package/template/modern.config.ts.handlebars +44 -23
  13. package/template/oxfmt.config.ts +8 -0
  14. package/template/oxlint.config.ts +12 -0
  15. package/template/package.json.handlebars +56 -27
  16. package/template/pnpm-workspace.yaml +24 -0
  17. package/template/rstest.config.mts +7 -0
  18. package/template/scripts/bootstrap-agent-skills.mjs +95 -0
  19. package/template/scripts/check-i18n-strings.mjs +83 -0
  20. package/template/scripts/validate-ultramodern.mjs.handlebars +350 -16
  21. package/template/shared/effect/api.ts.handlebars +1 -2
  22. package/template/src/modern-app-env.d.ts +2 -0
  23. package/template/src/modern.runtime.ts.handlebars +17 -3
  24. package/template/src/routes/[lang]/page.tsx.handlebars +211 -0
  25. package/template/src/routes/index.css.handlebars +14 -3
  26. package/template/src/routes/layout.tsx.handlebars +1 -1
  27. package/template/tests/tsconfig.json +7 -0
  28. package/template/tests/ultramodern.contract.test.ts +67 -0
  29. package/template/tsconfig.json +106 -2
  30. package/template-workspace/.agents/agent-reference-repos.json +23 -0
  31. package/template-workspace/.agents/rstackjs-agent-skills-LICENSE +21 -0
  32. package/template-workspace/.agents/skills/rsbuild-best-practices/SKILL.md +57 -0
  33. package/template-workspace/.agents/skills/rsdoctor-analysis/SKILL.md +96 -0
  34. package/template-workspace/.agents/skills/rsdoctor-analysis/references/command-map.md +113 -0
  35. package/template-workspace/.agents/skills/rsdoctor-analysis/references/common-analysis-patterns.md +190 -0
  36. package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor-common.md +88 -0
  37. package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor-rspack.md +138 -0
  38. package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor-webpack.md +71 -0
  39. package/template-workspace/.agents/skills/rsdoctor-analysis/references/install-rsdoctor.md +39 -0
  40. package/template-workspace/.agents/skills/rsdoctor-analysis/references/rsdoctor-data-types.md +103 -0
  41. package/template-workspace/.agents/skills/rslib-best-practices/SKILL.md +58 -0
  42. package/template-workspace/.agents/skills/rslib-modern-package/SKILL.md +173 -0
  43. package/template-workspace/.agents/skills/rspack-best-practices/SKILL.md +70 -0
  44. package/template-workspace/.agents/skills/rspack-tracing/SKILL.md +75 -0
  45. package/template-workspace/.agents/skills/rspack-tracing/references/bottlenecks.md +47 -0
  46. package/template-workspace/.agents/skills/rspack-tracing/references/tracing-guide.md +38 -0
  47. package/template-workspace/.agents/skills/rspack-tracing/scripts/analyze_trace.js +184 -0
  48. package/template-workspace/.agents/skills/rstest-best-practices/SKILL.md +133 -0
  49. package/template-workspace/.agents/skills-lock.json +95 -0
  50. package/template-workspace/.github/renovate.json +29 -0
  51. package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +52 -0
  52. package/template-workspace/.gitignore.handlebars +6 -0
  53. package/template-workspace/AGENTS.md +61 -0
  54. package/template-workspace/README.md.handlebars +11 -1
  55. package/template-workspace/oxfmt.config.ts +16 -0
  56. package/template-workspace/oxlint.config.ts +19 -0
  57. package/template-workspace/pnpm-workspace.yaml +24 -6
  58. package/template-workspace/scripts/bootstrap-agent-skills.mjs +95 -0
  59. package/template-workspace/scripts/check-i18n-strings.mjs +83 -0
  60. package/template-workspace/scripts/setup-agent-reference-repos.mjs +217 -0
  61. package/template-workspace/scripts/validate-ultramodern-workspace.mjs.handlebars +397 -59
  62. package/template/biome.json +0 -41
  63. package/template/src/routes/page.tsx.handlebars +0 -119
@@ -0,0 +1,95 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+
6
+ const root = process.cwd();
7
+ const lockPath = path.join(root, '.agents/skills-lock.json');
8
+ const checkOnly = process.argv.includes('--check');
9
+ const force = process.argv.includes('--force');
10
+
11
+ const readJson = (filePath) => JSON.parse(fs.readFileSync(filePath, 'utf-8'));
12
+
13
+ const run = (command, args, options = {}) =>
14
+ execFileSync(command, args, {
15
+ cwd: options.cwd ?? root,
16
+ encoding: 'utf-8',
17
+ stdio: options.stdio ?? ['ignore', 'pipe', 'pipe'],
18
+ });
19
+
20
+ const cloneSource = (source, targetDir) => {
21
+ const repo = source.repository.replace(/^https:\/\/github.com\//u, '');
22
+ try {
23
+ run('gh', ['repo', 'clone', repo, targetDir, '--', '--depth', '1'], {
24
+ stdio: 'inherit',
25
+ });
26
+ } catch {
27
+ run('git', ['clone', '--depth', '1', source.repository, targetDir], {
28
+ stdio: 'inherit',
29
+ });
30
+ }
31
+ };
32
+
33
+ const resolveSkillDir = (sourceRoot, skillName) => {
34
+ const candidates = [
35
+ path.join(sourceRoot, skillName),
36
+ path.join(sourceRoot, 'skills', skillName),
37
+ path.join(sourceRoot, 'skills', 'engineering', skillName),
38
+ path.join(sourceRoot, 'skills', 'productivity', skillName),
39
+ ];
40
+ return candidates.find((candidate) => fs.existsSync(path.join(candidate, 'SKILL.md')));
41
+ };
42
+
43
+ if (!fs.existsSync(lockPath)) {
44
+ console.error('Missing .agents/skills-lock.json');
45
+ process.exit(1);
46
+ }
47
+
48
+ const lock = readJson(lockPath);
49
+ const installDir = path.join(root, lock.installDir ?? '.agents/skills');
50
+ const privateSources = (lock.sources ?? []).filter(
51
+ (source) => source.install === 'clone-if-authorized',
52
+ );
53
+
54
+ if (checkOnly) {
55
+ const missing = privateSources.flatMap((source) =>
56
+ (source.baseline ?? [])
57
+ .map((skill) => skill.name)
58
+ .filter((skillName) => !fs.existsSync(path.join(installDir, skillName, 'SKILL.md'))),
59
+ );
60
+ if (missing.length > 0) {
61
+ console.warn(
62
+ `Private skills not installed: ${missing.join(', ')}. Run pnpm skills:install if you have access.`,
63
+ );
64
+ } else {
65
+ console.log('Agent skills are installed.');
66
+ }
67
+ process.exit(0);
68
+ }
69
+
70
+ fs.mkdirSync(installDir, { recursive: true });
71
+
72
+ for (const source of privateSources) {
73
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ultramodern-skills-'));
74
+ try {
75
+ cloneSource(source, tempDir);
76
+ for (const skill of source.baseline ?? []) {
77
+ const sourceSkillDir = resolveSkillDir(tempDir, skill.name);
78
+ if (!sourceSkillDir) {
79
+ throw new Error(`Skill ${skill.name} not found in ${source.repository}`);
80
+ }
81
+ const targetSkillDir = path.join(installDir, skill.name);
82
+ if (fs.existsSync(targetSkillDir)) {
83
+ if (!force) {
84
+ console.log(`Skipping existing ${skill.name}`);
85
+ continue;
86
+ }
87
+ fs.rmSync(targetSkillDir, { force: true, recursive: true });
88
+ }
89
+ fs.cpSync(sourceSkillDir, targetSkillDir, { recursive: true });
90
+ console.log(`Installed ${skill.name}`);
91
+ }
92
+ } finally {
93
+ fs.rmSync(tempDir, { force: true, recursive: true });
94
+ }
95
+ }
@@ -0,0 +1,83 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ const root = process.cwd();
5
+ const scanRoots = ['apps'].map((scanRoot) => path.join(root, scanRoot));
6
+ const ignoredDirectories = new Set(['.modern', '.modernjs', 'dist', 'node_modules']);
7
+ const visibleAttributePattern =
8
+ /\s(?:aria-label|alt|placeholder|title)=["']([^"']*[A-Za-z][^"']*)["']/gu;
9
+ const jsxTextPattern = />([^<>{}]*[A-Za-z][^<>{}]*)</gu;
10
+
11
+ const collectFiles = (directory) => {
12
+ if (!fs.existsSync(directory)) {
13
+ return [];
14
+ }
15
+
16
+ const files = [];
17
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
18
+ if (entry.isDirectory()) {
19
+ if (!ignoredDirectories.has(entry.name)) {
20
+ files.push(...collectFiles(path.join(directory, entry.name)));
21
+ }
22
+ continue;
23
+ }
24
+
25
+ if (entry.isFile() && /\.(jsx|tsx)$/u.test(entry.name) && !entry.name.endsWith('.d.ts')) {
26
+ files.push(path.join(directory, entry.name));
27
+ }
28
+ }
29
+ return files;
30
+ };
31
+
32
+ const lineNumberForIndex = (content, index) => content.slice(0, index).split('\n').length;
33
+ const isIgnoredLine = (content, index) => {
34
+ const lineStart = content.lastIndexOf('\n', index) + 1;
35
+ const lineEnd = content.indexOf('\n', index);
36
+ const currentLineEnd = lineEnd === -1 ? content.length : lineEnd;
37
+ const previousLineStart = content.lastIndexOf('\n', Math.max(0, lineStart - 2)) + 1;
38
+ const nextLineEnd = content.indexOf('\n', currentLineEnd + 1);
39
+ const context = content.slice(
40
+ previousLineStart,
41
+ nextLineEnd === -1 ? content.length : nextLineEnd,
42
+ );
43
+ return /i18n-ignore/u.test(context);
44
+ };
45
+
46
+ const violations = [];
47
+ for (const filePath of scanRoots.flatMap(collectFiles)) {
48
+ const content = fs.readFileSync(filePath, 'utf-8');
49
+ for (const match of content.matchAll(visibleAttributePattern)) {
50
+ if (!isIgnoredLine(content, match.index ?? 0)) {
51
+ violations.push({
52
+ filePath,
53
+ line: lineNumberForIndex(content, match.index ?? 0),
54
+ text: match[1].trim(),
55
+ });
56
+ }
57
+ }
58
+
59
+ for (const match of content.matchAll(jsxTextPattern)) {
60
+ const text = match[1].replaceAll(/\s+/gu, ' ').trim();
61
+ if (text && !isIgnoredLine(content, match.index ?? 0)) {
62
+ violations.push({
63
+ filePath,
64
+ line: lineNumberForIndex(content, match.index ?? 0),
65
+ text,
66
+ });
67
+ }
68
+ }
69
+ }
70
+
71
+ if (violations.length > 0) {
72
+ console.error('Hardcoded user-visible JSX strings found. Move copy to locale JSON files.');
73
+ for (const violation of violations) {
74
+ console.error(
75
+ `${path.relative(root, violation.filePath)}:${violation.line} ${JSON.stringify(
76
+ violation.text,
77
+ )}`,
78
+ );
79
+ }
80
+ process.exit(1);
81
+ }
82
+
83
+ console.log('No hardcoded user-visible JSX strings found.');
@@ -0,0 +1,217 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+
5
+ const root = process.cwd();
6
+ const args = new Set(process.argv.slice(2));
7
+ const checkOnly = args.has('--check');
8
+ const configPath = path.join(root, '.agents', 'agent-reference-repos.json');
9
+ const manifestPath = path.join(root, '.modernjs', 'agent-reference-repos.json');
10
+ const tempRoot = path.join(root, '.modernjs', 'agent-reference-repos-tmp');
11
+
12
+ const truthy = value => /^(1|true|yes|on)$/i.test(String(value ?? ''));
13
+ const falsy = value => /^(0|false|no|off)$/i.test(String(value ?? ''));
14
+
15
+ const skipRequested =
16
+ truthy(process.env.ULTRAMODERN_SKIP_AGENT_REPOS) ||
17
+ falsy(process.env.ULTRAMODERN_AGENT_REPOS);
18
+ const required = truthy(process.env.ULTRAMODERN_AGENT_REPOS_REQUIRED);
19
+ const refresh = truthy(process.env.ULTRAMODERN_AGENT_REPOS_REFRESH);
20
+
21
+ const log = message => console.log(`[agent-reference-repos] ${message}`);
22
+ const warn = message => console.warn(`[agent-reference-repos] ${message}`);
23
+
24
+ function fail(message) {
25
+ if (required || checkOnly) {
26
+ throw new Error(message);
27
+ }
28
+ warn(message);
29
+ }
30
+
31
+ function readJson(filePath) {
32
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
33
+ }
34
+
35
+ function run(command, commandArgs, options = {}) {
36
+ const result = spawnSync(command, commandArgs, {
37
+ cwd: options.cwd ?? root,
38
+ encoding: 'utf-8',
39
+ stdio: options.stdio ?? ['ignore', 'pipe', 'pipe'],
40
+ timeout: options.timeout ?? 120000,
41
+ });
42
+
43
+ if (result.error) {
44
+ throw result.error;
45
+ }
46
+ if (result.status !== 0) {
47
+ const stderr = result.stderr?.trim();
48
+ throw new Error(
49
+ `${command} ${commandArgs.join(' ')} failed${
50
+ stderr ? `: ${stderr}` : ''
51
+ }`,
52
+ );
53
+ }
54
+ return result.stdout?.trim() ?? '';
55
+ }
56
+
57
+ function assertSafeRepoPath(relativePath) {
58
+ if (
59
+ typeof relativePath !== 'string' ||
60
+ relativePath.length === 0 ||
61
+ path.isAbsolute(relativePath) ||
62
+ relativePath.split(/[\\/]+/).includes('..') ||
63
+ !relativePath.startsWith('repos/')
64
+ ) {
65
+ throw new Error(`Unsafe reference repository path: ${relativePath}`);
66
+ }
67
+ }
68
+
69
+ function hasGit() {
70
+ const result = spawnSync('git', ['--version'], {
71
+ encoding: 'utf-8',
72
+ stdio: ['ignore', 'pipe', 'pipe'],
73
+ });
74
+ return result.status === 0;
75
+ }
76
+
77
+ function existingReference(targetPath, repo) {
78
+ const markerPath = path.join(targetPath, '.ultramodern-reference-repo.json');
79
+ if (!fs.existsSync(markerPath)) {
80
+ return undefined;
81
+ }
82
+ try {
83
+ const marker = readJson(markerPath);
84
+ if (marker.url === repo.url && marker.ref === repo.ref) {
85
+ return marker;
86
+ }
87
+ } catch {
88
+ return undefined;
89
+ }
90
+ return undefined;
91
+ }
92
+
93
+ function materializeRepository(repo) {
94
+ assertSafeRepoPath(repo.path);
95
+ const targetPath = path.join(root, repo.path);
96
+ const existing = existingReference(targetPath, repo);
97
+
98
+ if (existing && !refresh) {
99
+ log(`${repo.id} already present at ${repo.path} (${existing.commit})`);
100
+ return { ...existing, status: 'present' };
101
+ }
102
+
103
+ if (fs.existsSync(targetPath)) {
104
+ if (!refresh) {
105
+ fail(`${repo.path} exists but is not a managed reference repo`);
106
+ return undefined;
107
+ }
108
+ fs.rmSync(targetPath, { recursive: true, force: true });
109
+ }
110
+
111
+ if (checkOnly) {
112
+ fail(`${repo.path} is missing`);
113
+ return undefined;
114
+ }
115
+
116
+ fs.mkdirSync(tempRoot, { recursive: true });
117
+ const tempPath = fs.mkdtempSync(path.join(tempRoot, `${repo.id}-`));
118
+
119
+ try {
120
+ log(`cloning ${repo.name} from ${repo.url}#${repo.ref}`);
121
+ run(
122
+ 'git',
123
+ [
124
+ 'clone',
125
+ '--depth',
126
+ '1',
127
+ '--single-branch',
128
+ '--branch',
129
+ repo.ref,
130
+ '--filter=blob:none',
131
+ repo.url,
132
+ tempPath,
133
+ ],
134
+ { timeout: 300000 },
135
+ );
136
+ const commit = run('git', ['-C', tempPath, 'rev-parse', 'HEAD']);
137
+ fs.rmSync(path.join(tempPath, '.git'), { recursive: true, force: true });
138
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
139
+ fs.renameSync(tempPath, targetPath);
140
+
141
+ const marker = {
142
+ schemaVersion: 1,
143
+ id: repo.id,
144
+ name: repo.name,
145
+ url: repo.url,
146
+ ref: repo.ref,
147
+ commit,
148
+ path: repo.path,
149
+ readOnly: repo.readOnly !== false,
150
+ installedAt: new Date().toISOString(),
151
+ };
152
+ fs.writeFileSync(
153
+ path.join(targetPath, '.ultramodern-reference-repo.json'),
154
+ `${JSON.stringify(marker, null, 2)}\n`,
155
+ );
156
+ log(`${repo.id} installed at ${repo.path} (${commit})`);
157
+ return { ...marker, status: 'installed' };
158
+ } catch (error) {
159
+ fs.rmSync(tempPath, { recursive: true, force: true });
160
+ fail(`Could not install ${repo.id}: ${error.message}`);
161
+ return undefined;
162
+ }
163
+ }
164
+
165
+ function main() {
166
+ if (!fs.existsSync(configPath)) {
167
+ fail('Missing .agents/agent-reference-repos.json');
168
+ return;
169
+ }
170
+
171
+ const config = readJson(configPath);
172
+ const enabled = config.defaultEnabled !== false && !skipRequested;
173
+
174
+ if (!enabled) {
175
+ log('setup skipped; set ULTRAMODERN_SKIP_AGENT_REPOS=0 to enable it again');
176
+ return;
177
+ }
178
+
179
+ if (!hasGit()) {
180
+ fail('git is required to install agent reference repositories');
181
+ return;
182
+ }
183
+
184
+ const installed = [];
185
+ for (const repo of config.repositories ?? []) {
186
+ const result = materializeRepository(repo);
187
+ if (result) {
188
+ installed.push(result);
189
+ }
190
+ }
191
+
192
+ if (!checkOnly) {
193
+ fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
194
+ fs.writeFileSync(
195
+ manifestPath,
196
+ `${JSON.stringify(
197
+ {
198
+ schemaVersion: 1,
199
+ generatedAt: new Date().toISOString(),
200
+ installDir: config.installDir ?? 'repos',
201
+ repositories: installed,
202
+ },
203
+ null,
204
+ 2,
205
+ )}\n`,
206
+ );
207
+ }
208
+ }
209
+
210
+ try {
211
+ main();
212
+ } catch (error) {
213
+ console.error(`[agent-reference-repos] ${error.message}`);
214
+ process.exitCode = 1;
215
+ } finally {
216
+ fs.rmSync(tempRoot, { recursive: true, force: true });
217
+ }