@bleedingdev/modern-js-create 3.2.0-ultramodern.99 → 3.4.0-ultramodern.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.
Files changed (179) hide show
  1. package/README.md +281 -106
  2. package/bin/run.js +1 -0
  3. package/dist/cjs/create-package-root.cjs +63 -0
  4. package/dist/cjs/index.cjs +658 -0
  5. package/dist/cjs/locale/en.cjs +101 -0
  6. package/dist/cjs/locale/index.cjs +50 -0
  7. package/dist/cjs/locale/zh.cjs +101 -0
  8. package/dist/cjs/ultramodern-package-source.cjs +135 -0
  9. package/dist/cjs/ultramodern-workspace/add-vertical.cjs +603 -0
  10. package/dist/cjs/ultramodern-workspace/app-files.cjs +223 -0
  11. package/dist/cjs/ultramodern-workspace/codesmith.cjs +134 -0
  12. package/dist/cjs/ultramodern-workspace/contracts.cjs +837 -0
  13. package/dist/cjs/ultramodern-workspace/demo-components.cjs +422 -0
  14. package/dist/cjs/ultramodern-workspace/descriptors.cjs +222 -0
  15. package/dist/cjs/ultramodern-workspace/effect-api.cjs +952 -0
  16. package/dist/cjs/ultramodern-workspace/fs-io.cjs +191 -0
  17. package/dist/cjs/ultramodern-workspace/generation-result.cjs +158 -0
  18. package/dist/cjs/ultramodern-workspace/index.cjs +51 -0
  19. package/dist/cjs/ultramodern-workspace/locales.cjs +173 -0
  20. package/dist/cjs/ultramodern-workspace/module-federation.cjs +487 -0
  21. package/dist/cjs/ultramodern-workspace/naming.cjs +161 -0
  22. package/dist/cjs/ultramodern-workspace/overlays.cjs +120 -0
  23. package/dist/cjs/ultramodern-workspace/package-json.cjs +406 -0
  24. package/dist/cjs/ultramodern-workspace/package-source.cjs +59 -0
  25. package/dist/cjs/ultramodern-workspace/policy.cjs +248 -0
  26. package/dist/cjs/ultramodern-workspace/public-api.cjs +47 -0
  27. package/dist/cjs/ultramodern-workspace/public-surface.cjs +268 -0
  28. package/dist/cjs/ultramodern-workspace/routes.cjs +375 -0
  29. package/dist/cjs/ultramodern-workspace/types.cjs +61 -0
  30. package/dist/cjs/ultramodern-workspace/versions.cjs +161 -0
  31. package/dist/cjs/ultramodern-workspace/workspace-scripts.cjs +154 -0
  32. package/dist/cjs/ultramodern-workspace/write-workspace.cjs +197 -0
  33. package/dist/esm/create-package-root.js +14 -0
  34. package/dist/esm/index.js +621 -0
  35. package/dist/esm/locale/en.js +63 -0
  36. package/dist/esm/locale/index.js +9 -0
  37. package/dist/esm/locale/zh.js +63 -0
  38. package/dist/esm/ultramodern-package-source.js +63 -0
  39. package/dist/esm/ultramodern-workspace/add-vertical.js +511 -0
  40. package/dist/esm/ultramodern-workspace/app-files.js +149 -0
  41. package/dist/esm/ultramodern-workspace/codesmith.js +86 -0
  42. package/dist/esm/ultramodern-workspace/contracts.js +742 -0
  43. package/dist/esm/ultramodern-workspace/demo-components.js +363 -0
  44. package/dist/esm/ultramodern-workspace/descriptors.js +133 -0
  45. package/dist/esm/ultramodern-workspace/effect-api.js +854 -0
  46. package/dist/esm/ultramodern-workspace/fs-io.js +90 -0
  47. package/dist/esm/ultramodern-workspace/generation-result.js +102 -0
  48. package/dist/esm/ultramodern-workspace/index.js +3 -0
  49. package/dist/esm/ultramodern-workspace/locales.js +122 -0
  50. package/dist/esm/ultramodern-workspace/module-federation.js +415 -0
  51. package/dist/esm/ultramodern-workspace/naming.js +71 -0
  52. package/dist/esm/ultramodern-workspace/overlays.js +79 -0
  53. package/dist/esm/ultramodern-workspace/package-json.js +338 -0
  54. package/dist/esm/ultramodern-workspace/package-source.js +21 -0
  55. package/dist/esm/ultramodern-workspace/policy.js +183 -0
  56. package/dist/esm/ultramodern-workspace/public-api.js +2 -0
  57. package/dist/esm/ultramodern-workspace/public-surface.js +183 -0
  58. package/dist/esm/ultramodern-workspace/routes.js +280 -0
  59. package/dist/esm/ultramodern-workspace/types.js +16 -0
  60. package/dist/esm/ultramodern-workspace/versions.js +36 -0
  61. package/dist/esm/ultramodern-workspace/workspace-scripts.js +92 -0
  62. package/dist/esm/ultramodern-workspace/write-workspace.js +143 -0
  63. package/dist/esm-node/create-package-root.js +15 -0
  64. package/dist/esm-node/index.js +622 -0
  65. package/dist/esm-node/locale/en.js +64 -0
  66. package/dist/esm-node/locale/index.js +10 -0
  67. package/dist/esm-node/locale/zh.js +64 -0
  68. package/dist/esm-node/ultramodern-package-source.js +64 -0
  69. package/dist/esm-node/ultramodern-workspace/add-vertical.js +512 -0
  70. package/dist/esm-node/ultramodern-workspace/app-files.js +150 -0
  71. package/dist/esm-node/ultramodern-workspace/codesmith.js +87 -0
  72. package/dist/esm-node/ultramodern-workspace/contracts.js +743 -0
  73. package/dist/esm-node/ultramodern-workspace/demo-components.js +364 -0
  74. package/dist/esm-node/ultramodern-workspace/descriptors.js +134 -0
  75. package/dist/esm-node/ultramodern-workspace/effect-api.js +855 -0
  76. package/dist/esm-node/ultramodern-workspace/fs-io.js +91 -0
  77. package/dist/esm-node/ultramodern-workspace/generation-result.js +103 -0
  78. package/dist/esm-node/ultramodern-workspace/index.js +4 -0
  79. package/dist/esm-node/ultramodern-workspace/locales.js +123 -0
  80. package/dist/esm-node/ultramodern-workspace/module-federation.js +416 -0
  81. package/dist/esm-node/ultramodern-workspace/naming.js +72 -0
  82. package/dist/esm-node/ultramodern-workspace/overlays.js +80 -0
  83. package/dist/esm-node/ultramodern-workspace/package-json.js +339 -0
  84. package/dist/esm-node/ultramodern-workspace/package-source.js +22 -0
  85. package/dist/esm-node/ultramodern-workspace/policy.js +184 -0
  86. package/dist/esm-node/ultramodern-workspace/public-api.js +3 -0
  87. package/dist/esm-node/ultramodern-workspace/public-surface.js +184 -0
  88. package/dist/esm-node/ultramodern-workspace/routes.js +281 -0
  89. package/dist/esm-node/ultramodern-workspace/types.js +17 -0
  90. package/dist/esm-node/ultramodern-workspace/versions.js +37 -0
  91. package/dist/esm-node/ultramodern-workspace/workspace-scripts.js +93 -0
  92. package/dist/esm-node/ultramodern-workspace/write-workspace.js +144 -0
  93. package/dist/types/create-package-root.d.ts +1 -0
  94. package/dist/types/locale/en.d.ts +12 -8
  95. package/dist/types/locale/index.d.ts +125 -2
  96. package/dist/types/locale/zh.d.ts +12 -8
  97. package/dist/types/ultramodern-package-source.d.ts +28 -0
  98. package/dist/types/ultramodern-workspace/add-vertical.d.ts +36 -0
  99. package/dist/types/ultramodern-workspace/app-files.d.ts +14 -0
  100. package/dist/types/ultramodern-workspace/codesmith.d.ts +53 -0
  101. package/dist/types/ultramodern-workspace/contracts.d.ts +21 -0
  102. package/dist/types/ultramodern-workspace/demo-components.d.ts +9 -0
  103. package/dist/types/ultramodern-workspace/descriptors.d.ts +39 -0
  104. package/dist/types/ultramodern-workspace/effect-api.d.ts +73 -0
  105. package/dist/types/ultramodern-workspace/fs-io.d.ts +18 -0
  106. package/dist/types/ultramodern-workspace/generation-result.d.ts +14 -0
  107. package/dist/types/ultramodern-workspace/index.d.ts +4 -0
  108. package/dist/types/ultramodern-workspace/locales.d.ts +183 -0
  109. package/dist/types/ultramodern-workspace/module-federation.d.ts +16 -0
  110. package/dist/types/ultramodern-workspace/naming.d.ts +16 -0
  111. package/dist/types/ultramodern-workspace/overlays.d.ts +6 -0
  112. package/dist/types/ultramodern-workspace/package-json.d.ts +12 -0
  113. package/dist/types/ultramodern-workspace/package-source.d.ts +2 -0
  114. package/dist/types/ultramodern-workspace/policy.d.ts +60 -0
  115. package/dist/types/ultramodern-workspace/public-api.d.ts +3 -0
  116. package/dist/types/ultramodern-workspace/public-surface.d.ts +37 -0
  117. package/dist/types/ultramodern-workspace/routes.d.ts +25 -0
  118. package/dist/types/ultramodern-workspace/types.d.ts +197 -0
  119. package/dist/types/ultramodern-workspace/versions.d.ts +40 -0
  120. package/dist/types/ultramodern-workspace/workspace-scripts.d.ts +10 -0
  121. package/dist/types/ultramodern-workspace/write-workspace.d.ts +4 -0
  122. package/package.json +58 -15
  123. package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +23 -10
  124. package/template-workspace/.mise.toml.handlebars +1 -0
  125. package/template-workspace/{AGENTS.md → AGENTS.md.handlebars} +20 -9
  126. package/template-workspace/README.md.handlebars +85 -14
  127. package/template-workspace/lefthook.yml +18 -4
  128. package/template-workspace/pnpm-workspace.yaml.handlebars +45 -0
  129. package/template-workspace/scripts/bootstrap-agent-skills.mjs +69 -75
  130. package/template-workspace/scripts/setup-agent-reference-repos.mjs +8 -6
  131. package/templates/app/shell-frame.tsx +49 -0
  132. package/templates/app/ultramodern-route-head.tsx.handlebars +142 -0
  133. package/templates/packages/shared-contracts-index.ts +466 -0
  134. package/templates/workspace-scripts/assert-mf-types.mjs.handlebars +69 -0
  135. package/templates/workspace-scripts/check-ultramodern-i18n-boundaries.mjs +9 -0
  136. package/templates/workspace-scripts/generate-public-surface-assets.mjs +529 -0
  137. package/templates/workspace-scripts/proof-cloudflare-version.mjs +125 -0
  138. package/templates/workspace-scripts/ultramodern-cloudflare-proof.mjs +851 -0
  139. package/templates/workspace-scripts/ultramodern-performance-readiness.config.mjs +7 -0
  140. package/templates/workspace-scripts/ultramodern-performance-readiness.mjs +223 -0
  141. package/templates/workspace-scripts/validate-ultramodern-workspace.mjs.handlebars +986 -0
  142. package/dist/index.js +0 -6625
  143. package/dist/types/ultramodern-workspace.d.ts +0 -31
  144. package/template/.agents/skills-lock.json +0 -34
  145. package/template/.browserslistrc +0 -4
  146. package/template/.codex/hooks.json +0 -16
  147. package/template/.github/renovate.json +0 -53
  148. package/template/.github/workflows/ultramodern-gates.yml.handlebars +0 -54
  149. package/template/.gitignore.handlebars +0 -30
  150. package/template/.mise.toml.handlebars +0 -2
  151. package/template/.nvmrc +0 -2
  152. package/template/AGENTS.md +0 -23
  153. package/template/README.md +0 -105
  154. package/template/api/effect/index.ts.handlebars +0 -34
  155. package/template/api/lambda/hello.ts.handlebars +0 -6
  156. package/template/config/public/locales/cs/translation.json +0 -39
  157. package/template/config/public/locales/en/translation.json +0 -39
  158. package/template/lefthook.yml +0 -10
  159. package/template/modern.config.ts.handlebars +0 -71
  160. package/template/oxfmt.config.ts +0 -15
  161. package/template/oxlint.config.ts +0 -19
  162. package/template/package.json.handlebars +0 -66
  163. package/template/pnpm-workspace.yaml +0 -29
  164. package/template/postcss.config.mjs.handlebars +0 -6
  165. package/template/rstest.config.mts +0 -5
  166. package/template/scripts/bootstrap-agent-skills.mjs +0 -228
  167. package/template/scripts/check-i18n-strings.mjs +0 -94
  168. package/template/scripts/validate-ultramodern.mjs.handlebars +0 -538
  169. package/template/shared/effect/api.ts.handlebars +0 -17
  170. package/template/src/modern-app-env.d.ts +0 -3
  171. package/template/src/modern.runtime.ts.handlebars +0 -23
  172. package/template/src/routes/[lang]/page.tsx.handlebars +0 -211
  173. package/template/src/routes/index.css.handlebars +0 -129
  174. package/template/src/routes/layout.tsx.handlebars +0 -10
  175. package/template/tailwind.config.ts.handlebars +0 -10
  176. package/template/tests/tsconfig.json +0 -7
  177. package/template/tests/ultramodern.contract.test.ts.handlebars +0 -78
  178. package/template/tsconfig.json +0 -120
  179. package/template-workspace/pnpm-workspace.yaml +0 -35
@@ -1,29 +0,0 @@
1
- minimumReleaseAge: 1440
2
- minimumReleaseAgeStrict: true
3
- minimumReleaseAgeIgnoreMissingTime: false
4
- minimumReleaseAgeExclude:
5
- - '@bleedingdev/modern-js-*'
6
- trustPolicy: no-downgrade
7
- trustPolicyIgnoreAfter: 1440
8
- blockExoticSubdeps: true
9
- engineStrict: true
10
- pmOnFail: error
11
- verifyDepsBeforeRun: error
12
- strictDepBuilds: true
13
-
14
- allowBuilds:
15
- '@swc/core': true
16
- core-js: true
17
- esbuild: true
18
- lefthook: true
19
- msgpackr-extract: true
20
- sharp: true
21
- workerd: true
22
- onlyBuiltDependencies:
23
- - '@swc/core'
24
- - core-js
25
- - esbuild
26
- - lefthook
27
- - msgpackr-extract
28
- - sharp
29
- - workerd
@@ -1,6 +0,0 @@
1
- {{#if enableTailwind}}export default {
2
- plugins: {
3
- '@tailwindcss/postcss': {},
4
- },
5
- };
6
- {{/if}}
@@ -1,5 +0,0 @@
1
- import { defineConfig } from '@rstest/core';
2
-
3
- export default defineConfig({
4
- testEnvironment: 'node',
5
- });
@@ -1,228 +0,0 @@
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 commandExists = (command) => {
21
- try {
22
- run(command, ['--version'], { stdio: 'ignore' });
23
- return true;
24
- } catch {
25
- return false;
26
- }
27
- };
28
-
29
- const runShell = (script) =>
30
- run('sh', ['-lc', script], {
31
- stdio: 'inherit',
32
- });
33
-
34
- const installGit = () => {
35
- if (commandExists('git')) {
36
- return;
37
- }
38
-
39
- if (commandExists('brew')) {
40
- run('brew', ['install', 'git'], { stdio: 'inherit' });
41
- } else if (process.platform === 'linux' && commandExists('apt-get')) {
42
- const sudo = typeof process.getuid === 'function' && process.getuid() === 0 ? '' : 'sudo ';
43
- runShell(`${sudo}apt-get update && ${sudo}apt-get install -y git`);
44
- } else if (process.platform === 'linux' && commandExists('dnf')) {
45
- const sudo = typeof process.getuid === 'function' && process.getuid() === 0 ? '' : 'sudo ';
46
- runShell(`${sudo}dnf install -y git`);
47
- } else if (process.platform === 'linux' && commandExists('yum')) {
48
- const sudo = typeof process.getuid === 'function' && process.getuid() === 0 ? '' : 'sudo ';
49
- runShell(`${sudo}yum install -y git`);
50
- } else if (process.platform === 'linux' && commandExists('apk')) {
51
- runShell('apk add --no-cache git');
52
- }
53
-
54
- if (!commandExists('git')) {
55
- throw new Error(
56
- 'Git is required for UltraModern setup. Install git and run pnpm skills:install again.',
57
- );
58
- }
59
- };
60
-
61
- const isInsideGitWorkTree = () => {
62
- try {
63
- return run('git', ['rev-parse', '--is-inside-work-tree']).trim() === 'true';
64
- } catch {
65
- return false;
66
- }
67
- };
68
-
69
- const initializeGitRepository = () => {
70
- if (isInsideGitWorkTree()) {
71
- return;
72
- }
73
-
74
- try {
75
- run('git', ['init', '-b', 'main'], { stdio: 'inherit' });
76
- } catch {
77
- run('git', ['init'], { stdio: 'inherit' });
78
- run('git', ['branch', '-M', 'main'], { stdio: 'inherit' });
79
- }
80
- };
81
-
82
- const installLefthook = () => {
83
- try {
84
- run('lefthook', ['install'], { stdio: 'inherit' });
85
- } catch (error) {
86
- console.warn(`Unable to install lefthook hooks: ${error.message}`);
87
- }
88
- };
89
-
90
- const removeTree = (dir) =>
91
- fs.rmSync(dir, {
92
- force: true,
93
- maxRetries: 5,
94
- recursive: true,
95
- retryDelay: 100,
96
- });
97
-
98
- const cloneSource = (source, targetDir) => {
99
- if (source.commit) {
100
- run('git', ['init', targetDir]);
101
- run('git', ['remote', 'add', 'origin', source.repository], {
102
- cwd: targetDir,
103
- });
104
- run('git', ['fetch', '--depth', '1', '--quiet', 'origin', source.commit], {
105
- cwd: targetDir,
106
- });
107
- run(
108
- 'git',
109
- [
110
- '-c',
111
- 'advice.detachedHead=false',
112
- 'checkout',
113
- '--detach',
114
- '--quiet',
115
- 'FETCH_HEAD',
116
- ],
117
- { cwd: targetDir },
118
- );
119
- return;
120
- }
121
-
122
- const repo = source.repository.replace(/^https:\/\/github.com\//u, '');
123
- try {
124
- run('gh', ['repo', 'clone', repo, targetDir, '--', '--depth', '1', '--quiet']);
125
- } catch {
126
- run('git', ['clone', '--depth', '1', '--quiet', source.repository, targetDir]);
127
- }
128
- };
129
-
130
- const resolveSkillDir = (sourceRoot, skillName) => {
131
- const candidates = [
132
- path.join(sourceRoot, skillName),
133
- path.join(sourceRoot, 'skills', skillName),
134
- path.join(sourceRoot, 'skills', 'engineering', skillName),
135
- path.join(sourceRoot, 'skills', 'productivity', skillName),
136
- ];
137
- return candidates.find((candidate) => fs.existsSync(path.join(candidate, 'SKILL.md')));
138
- };
139
-
140
- if (!fs.existsSync(lockPath)) {
141
- console.error('Missing .agents/skills-lock.json');
142
- process.exit(1);
143
- }
144
-
145
- const lock = readJson(lockPath);
146
- const installDir = path.join(root, lock.installDir ?? '.agents/skills');
147
- const sources = lock.sources ?? [];
148
- const requiredCloneSources = sources.filter((source) => source.install === 'clone');
149
- const optionalCloneSources = sources.filter(
150
- (source) => source.install === 'clone-if-authorized',
151
- );
152
- const requiredSkills = [
153
- ...(lock.baseline ?? []),
154
- ...requiredCloneSources.flatMap((source) => source.baseline ?? []),
155
- ].filter(
156
- (skill, index, skills) =>
157
- skills.findIndex((candidate) => candidate.name === skill.name) === index,
158
- );
159
-
160
- if (checkOnly) {
161
- const missingRequired = requiredSkills
162
- .map((skill) => skill.name)
163
- .filter((skillName) => !fs.existsSync(path.join(installDir, skillName, 'SKILL.md')));
164
- const missingOptional = optionalCloneSources.flatMap((source) =>
165
- (source.baseline ?? [])
166
- .map((skill) => skill.name)
167
- .filter((skillName) => !fs.existsSync(path.join(installDir, skillName, 'SKILL.md'))),
168
- );
169
-
170
- if (missingRequired.length > 0) {
171
- console.error(
172
- `Required agent skills not installed: ${missingRequired.join(', ')}. Run pnpm skills:install.`,
173
- );
174
- process.exit(1);
175
- }
176
-
177
- if (missingOptional.length > 0) {
178
- console.warn(
179
- `Private skills not installed: ${missingOptional.join(', ')}. Run pnpm skills:install if you have access.`,
180
- );
181
- } else {
182
- console.log('Required and private agent skills are installed.');
183
- process.exit(0);
184
- }
185
- console.log('Required agent skills are installed.');
186
- process.exit(0);
187
- }
188
-
189
- fs.mkdirSync(installDir, { recursive: true });
190
- installGit();
191
-
192
- for (const source of [...requiredCloneSources, ...optionalCloneSources]) {
193
- const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ultramodern-skills-'));
194
- try {
195
- try {
196
- cloneSource(source, tempDir);
197
- } catch (error) {
198
- if (source.install === 'clone-if-authorized') {
199
- console.warn(
200
- `Skipping ${source.repository}; current developer may not have access.`,
201
- );
202
- continue;
203
- }
204
- throw error;
205
- }
206
- for (const skill of source.baseline ?? []) {
207
- const sourceSkillDir = resolveSkillDir(tempDir, skill.name);
208
- if (!sourceSkillDir) {
209
- throw new Error(`Skill ${skill.name} not found in ${source.repository}`);
210
- }
211
- const targetSkillDir = path.join(installDir, skill.name);
212
- if (fs.existsSync(targetSkillDir)) {
213
- if (!force) {
214
- console.log(`Skipping existing ${skill.name}`);
215
- continue;
216
- }
217
- removeTree(targetSkillDir);
218
- }
219
- fs.cpSync(sourceSkillDir, targetSkillDir, { recursive: true });
220
- console.log(`Installed ${skill.name}`);
221
- }
222
- } finally {
223
- removeTree(tempDir);
224
- }
225
- }
226
-
227
- initializeGitRepository();
228
- installLefthook();
@@ -1,94 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
-
4
- const root = process.cwd();
5
- const scanRoots = ['src'].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 isCodeElementText = (content, index) => {
34
- const tagStart = content.lastIndexOf('<', index);
35
- if (tagStart === -1) {
36
- return false;
37
- }
38
- return /^<code(?:\s|>)/u.test(content.slice(tagStart, index));
39
- };
40
- const isIgnoredLine = (content, index) => {
41
- const lineStart = content.lastIndexOf('\n', index) + 1;
42
- const lineEnd = content.indexOf('\n', index);
43
- const currentLineEnd = lineEnd === -1 ? content.length : lineEnd;
44
- const previousLineStart = content.lastIndexOf('\n', Math.max(0, lineStart - 2)) + 1;
45
- const nextLineEnd = content.indexOf('\n', currentLineEnd + 1);
46
- const context = content.slice(
47
- previousLineStart,
48
- nextLineEnd === -1 ? content.length : nextLineEnd,
49
- );
50
- return /i18n-ignore/u.test(context);
51
- };
52
-
53
- const violations = [];
54
- for (const filePath of scanRoots.flatMap(collectFiles)) {
55
- const content = fs.readFileSync(filePath, 'utf-8');
56
- for (const match of content.matchAll(visibleAttributePattern)) {
57
- if (!isIgnoredLine(content, match.index ?? 0)) {
58
- violations.push({
59
- filePath,
60
- line: lineNumberForIndex(content, match.index ?? 0),
61
- text: match[1].trim(),
62
- });
63
- }
64
- }
65
-
66
- for (const match of content.matchAll(jsxTextPattern)) {
67
- const text = match[1].replaceAll(/\s+/gu, ' ').trim();
68
- if (
69
- text &&
70
- !isIgnoredLine(content, match.index ?? 0) &&
71
- !isCodeElementText(content, match.index ?? 0)
72
- ) {
73
- violations.push({
74
- filePath,
75
- line: lineNumberForIndex(content, match.index ?? 0),
76
- text,
77
- });
78
- }
79
- }
80
- }
81
-
82
- if (violations.length > 0) {
83
- console.error('Hardcoded user-visible JSX strings found. Move copy to locale JSON files.');
84
- for (const violation of violations) {
85
- console.error(
86
- `${path.relative(root, violation.filePath)}:${violation.line} ${JSON.stringify(
87
- violation.text,
88
- )}`,
89
- );
90
- }
91
- process.exit(1);
92
- }
93
-
94
- console.log('No hardcoded user-visible JSX strings found.');