@bleedingdev/modern-js-create 3.2.0-ultramodern.103 → 3.2.0-ultramodern.105
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 +9 -3
- package/dist/index.js +483 -144
- package/dist/types/ultramodern-package-source.d.ts +28 -0
- package/dist/types/ultramodern-workspace.d.ts +1 -2
- package/package.json +4 -4
- package/template/modern.config.ts.handlebars +8 -8
- package/template/package.json.handlebars +14 -14
- package/template/pnpm-workspace.yaml +5 -0
- package/template/scripts/check-i18n-strings.mjs +114 -2
- package/template/scripts/validate-ultramodern.mjs.handlebars +50 -42
- package/template/tests/ultramodern.contract.test.ts.handlebars +14 -8
- package/template-workspace/README.md.handlebars +12 -3
- package/template-workspace/pnpm-workspace.yaml +6 -1
- package/template-workspace/scripts/setup-agent-reference-repos.mjs +8 -6
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare const WORKSPACE_PACKAGE_VERSION = "workspace:*";
|
|
2
|
+
export declare const BLEEDINGDEV_CREATE_PACKAGE = "@bleedingdev/modern-js-create";
|
|
3
|
+
export declare const BLEEDINGDEV_PACKAGE_SCOPE = "bleedingdev";
|
|
4
|
+
export declare const BLEEDINGDEV_PACKAGE_NAME_PREFIX = "modern-js-";
|
|
5
|
+
export declare const BLEEDINGDEV_FRAMEWORK_VERSION_ENV = "MODERN_CREATE_ULTRAMODERN_FRAMEWORK_VERSION";
|
|
6
|
+
export declare const ULTRAMODERN_SINGLE_APP_MODERN_PACKAGES: readonly ['@modern-js/runtime', '@modern-js/app-tools', '@modern-js/tsconfig', '@modern-js/plugin-i18n', '@modern-js/plugin-tanstack', '@modern-js/plugin-bff', '@modern-js/adapter-rstest'];
|
|
7
|
+
export declare const ULTRAMODERN_WORKSPACE_MODERN_PACKAGES: readonly ['@modern-js/app-tools', '@modern-js/plugin-bff', '@modern-js/plugin-i18n', '@modern-js/plugin-tanstack', '@modern-js/runtime'];
|
|
8
|
+
export type UltramodernPackageSourceStrategy = 'workspace' | 'install';
|
|
9
|
+
export type ResolvedUltramodernPackageSource = {
|
|
10
|
+
strategy: UltramodernPackageSourceStrategy;
|
|
11
|
+
modernPackageVersion: string;
|
|
12
|
+
registry?: string;
|
|
13
|
+
aliasScope?: string;
|
|
14
|
+
aliasPackageNamePrefix?: string;
|
|
15
|
+
};
|
|
16
|
+
export type UltramodernModernPackagesMetadata = {
|
|
17
|
+
packages: string[];
|
|
18
|
+
specifier: string;
|
|
19
|
+
registry?: string;
|
|
20
|
+
aliases?: Record<string, string>;
|
|
21
|
+
};
|
|
22
|
+
export declare function modernPackageVersion(packageSource: ResolvedUltramodernPackageSource): string;
|
|
23
|
+
export declare function modernAliasPackageName(packageName: string, packageSource: ResolvedUltramodernPackageSource): string;
|
|
24
|
+
export declare function modernPackageSpecifier(packageName: string, packageSource: ResolvedUltramodernPackageSource): string;
|
|
25
|
+
export declare function modernPackageAliases(packageNames: readonly string[], packageSource: ResolvedUltramodernPackageSource): Record<string, string> | undefined;
|
|
26
|
+
export declare function createModernPackagesMetadata(packageNames: readonly string[], packageSource: ResolvedUltramodernPackageSource, options?: {
|
|
27
|
+
includeAliases?: boolean;
|
|
28
|
+
}): UltramodernModernPackagesMetadata;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type UltramodernPackageSourceStrategy
|
|
1
|
+
import { type UltramodernPackageSourceStrategy } from './ultramodern-package-source';
|
|
2
2
|
export type UltramodernWorkspaceOptions = {
|
|
3
3
|
targetDir: string;
|
|
4
4
|
packageName: string;
|
|
@@ -28,4 +28,3 @@ export declare const ultramodernWorkspaceVersions: {
|
|
|
28
28
|
tailwind: string;
|
|
29
29
|
tailwindPostcss: string;
|
|
30
30
|
};
|
|
31
|
-
export {};
|
package/package.json
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"engines": {
|
|
22
22
|
"node": ">=20"
|
|
23
23
|
},
|
|
24
|
-
"version": "3.2.0-ultramodern.
|
|
24
|
+
"version": "3.2.0-ultramodern.105",
|
|
25
25
|
"types": "./dist/types/index.d.ts",
|
|
26
26
|
"main": "./dist/index.js",
|
|
27
27
|
"bin": {
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@rslib/core": "0.21.5",
|
|
41
41
|
"@types/node": "^25.9.1",
|
|
42
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
42
|
+
"@typescript/native-preview": "7.0.0-dev.20260606.1",
|
|
43
43
|
"tsx": "^4.22.3",
|
|
44
|
-
"@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.2.0-ultramodern.
|
|
44
|
+
"@modern-js/i18n-utils": "npm:@bleedingdev/modern-js-i18n-utils@3.2.0-ultramodern.105"
|
|
45
45
|
},
|
|
46
46
|
"publishConfig": {
|
|
47
47
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -54,6 +54,6 @@
|
|
|
54
54
|
"start": "node ./dist/index.js"
|
|
55
55
|
},
|
|
56
56
|
"ultramodern": {
|
|
57
|
-
"frameworkVersion": "3.2.0-ultramodern.
|
|
57
|
+
"frameworkVersion": "3.2.0-ultramodern.105"
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -39,7 +39,13 @@ export default defineConfig(
|
|
|
39
39
|
{{/if}} runtimeFramework: '{{bffRuntime}}',
|
|
40
40
|
},
|
|
41
41
|
|
|
42
|
-
{{/if}}
|
|
42
|
+
{{/if}} html: {
|
|
43
|
+
meta: {
|
|
44
|
+
viewport: 'width=device-width, initial-scale=1.0, viewport-fit=cover',
|
|
45
|
+
},
|
|
46
|
+
title: 'UltraModern.js Starter',
|
|
47
|
+
},
|
|
48
|
+
plugins: [
|
|
43
49
|
appTools(),
|
|
44
50
|
i18nPlugin({
|
|
45
51
|
localeDetection: {
|
|
@@ -53,12 +59,6 @@ export default defineConfig(
|
|
|
53
59
|
{{/if}}{{#if enableBff}}
|
|
54
60
|
bffPlugin(),
|
|
55
61
|
{{/if}} ],
|
|
56
|
-
html: {
|
|
57
|
-
title: 'UltraModern.js Starter',
|
|
58
|
-
meta: {
|
|
59
|
-
viewport: 'width=device-width, initial-scale=1.0, viewport-fit=cover',
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
62
|
source: {
|
|
63
63
|
globalVars: {
|
|
64
64
|
ULTRAMODERN_SITE_URL: siteUrl,
|
|
@@ -70,8 +70,8 @@ export default defineConfig(
|
|
|
70
70
|
enableBffRequestId,
|
|
71
71
|
enableModuleFederationSSR,
|
|
72
72
|
enableTelemetryExporters,
|
|
73
|
-
telemetryFailLoudStartup,
|
|
74
73
|
...(typeof otlpEndpoint === 'string' ? { otlpEndpoint } : {}),
|
|
74
|
+
telemetryFailLoudStartup,
|
|
75
75
|
...(typeof victoriaMetricsEndpoint === 'string' ? { victoriaMetricsEndpoint } : {}),
|
|
76
76
|
},
|
|
77
77
|
),
|
|
@@ -30,34 +30,34 @@
|
|
|
30
30
|
"@modern-js/runtime": "{{runtimeVersion}}",
|
|
31
31
|
{{#if isTanstackRouter}} "@tanstack/react-router": "{{tanstackRouterVersion}}",
|
|
32
32
|
{{/if}}
|
|
33
|
-
"i18next": "
|
|
34
|
-
"react": "
|
|
35
|
-
"react-dom": "
|
|
36
|
-
"react-i18next": "
|
|
33
|
+
"i18next": "{{i18nextVersion}}",
|
|
34
|
+
"react": "{{reactVersion}}",
|
|
35
|
+
"react-dom": "{{reactDomVersion}}",
|
|
36
|
+
"react-i18next": "{{reactI18nextVersion}}"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@effect/tsgo": "
|
|
39
|
+
"@effect/tsgo": "{{effectTsgoVersion}}",
|
|
40
40
|
"@modern-js/adapter-rstest": "{{adapterRstestVersion}}",
|
|
41
41
|
"@modern-js/app-tools": "{{appToolsVersion}}",
|
|
42
42
|
{{#if enableBff}} "@modern-js/plugin-bff": "{{pluginBffVersion}}",
|
|
43
43
|
{{/if}} "@modern-js/tsconfig": "{{tsconfigVersion}}",
|
|
44
|
-
"@rstest/core": "
|
|
44
|
+
"@rstest/core": "{{rstestCoreVersion}}",
|
|
45
45
|
{{#if enableTailwind}}
|
|
46
46
|
"@tailwindcss/postcss": "^{{tailwindPostcssVersion}}",
|
|
47
47
|
{{/if}}
|
|
48
48
|
"@types/node": "^20",
|
|
49
|
-
"@types/react": "
|
|
50
|
-
"@types/react-dom": "
|
|
51
|
-
"@typescript/native-preview": "
|
|
52
|
-
"happy-dom": "
|
|
49
|
+
"@types/react": "{{typesReactVersion}}",
|
|
50
|
+
"@types/react-dom": "{{typesReactDomVersion}}",
|
|
51
|
+
"@typescript/native-preview": "{{typescriptNativePreviewVersion}}",
|
|
52
|
+
"happy-dom": "{{happyDomVersion}}",
|
|
53
53
|
{{#unless isSubproject}}
|
|
54
54
|
"lefthook": "^2.1.9",
|
|
55
|
-
"oxfmt": "
|
|
56
|
-
"oxlint": "
|
|
57
|
-
{{/unless}}{{#if enableTailwind}} "postcss": "
|
|
55
|
+
"oxfmt": "{{oxfmtVersion}}",
|
|
56
|
+
"oxlint": "{{oxlintVersion}}",
|
|
57
|
+
{{/unless}}{{#if enableTailwind}} "postcss": "{{postcssVersion}}",
|
|
58
58
|
{{/if}} "rimraf": "^6.1.3"{{#if enableTailwind}},
|
|
59
59
|
"tailwindcss": "^{{tailwindVersion}}"{{/if}}{{#unless isSubproject}},
|
|
60
|
-
"ultracite": "
|
|
60
|
+
"ultracite": "{{ultraciteVersion}}"{{/unless}}
|
|
61
61
|
},
|
|
62
62
|
"engines": {
|
|
63
63
|
"node": ">=20",
|
|
@@ -3,6 +3,11 @@ minimumReleaseAgeStrict: true
|
|
|
3
3
|
minimumReleaseAgeIgnoreMissingTime: false
|
|
4
4
|
minimumReleaseAgeExclude:
|
|
5
5
|
- '@bleedingdev/modern-js-*'
|
|
6
|
+
- '@tanstack/react-router'
|
|
7
|
+
- '@tanstack/router-core'
|
|
8
|
+
- '@typescript/native-preview'
|
|
9
|
+
- '@typescript/native-preview-*'
|
|
10
|
+
- '@types/react'
|
|
6
11
|
trustPolicy: no-downgrade
|
|
7
12
|
trustPolicyIgnoreAfter: 1440
|
|
8
13
|
blockExoticSubdeps: true
|
|
@@ -7,6 +7,97 @@ const ignoredDirectories = new Set(['.modern', '.modernjs', 'dist', 'node_module
|
|
|
7
7
|
const visibleAttributePattern =
|
|
8
8
|
/\s(?:aria-label|alt|placeholder|title)=["']([^"']*[A-Za-z][^"']*)["']/gu;
|
|
9
9
|
const jsxTextPattern = />([^<>{}]*[A-Za-z][^<>{}]*)</gu;
|
|
10
|
+
const jsxIntrinsicTags = new Set([
|
|
11
|
+
'a',
|
|
12
|
+
'abbr',
|
|
13
|
+
'address',
|
|
14
|
+
'area',
|
|
15
|
+
'article',
|
|
16
|
+
'aside',
|
|
17
|
+
'audio',
|
|
18
|
+
'b',
|
|
19
|
+
'blockquote',
|
|
20
|
+
'body',
|
|
21
|
+
'br',
|
|
22
|
+
'button',
|
|
23
|
+
'canvas',
|
|
24
|
+
'caption',
|
|
25
|
+
'cite',
|
|
26
|
+
'code',
|
|
27
|
+
'col',
|
|
28
|
+
'colgroup',
|
|
29
|
+
'data',
|
|
30
|
+
'datalist',
|
|
31
|
+
'dd',
|
|
32
|
+
'del',
|
|
33
|
+
'details',
|
|
34
|
+
'dfn',
|
|
35
|
+
'dialog',
|
|
36
|
+
'div',
|
|
37
|
+
'dl',
|
|
38
|
+
'dt',
|
|
39
|
+
'em',
|
|
40
|
+
'fieldset',
|
|
41
|
+
'figcaption',
|
|
42
|
+
'figure',
|
|
43
|
+
'footer',
|
|
44
|
+
'form',
|
|
45
|
+
'h1',
|
|
46
|
+
'h2',
|
|
47
|
+
'h3',
|
|
48
|
+
'h4',
|
|
49
|
+
'h5',
|
|
50
|
+
'h6',
|
|
51
|
+
'head',
|
|
52
|
+
'header',
|
|
53
|
+
'hr',
|
|
54
|
+
'html',
|
|
55
|
+
'i',
|
|
56
|
+
'iframe',
|
|
57
|
+
'img',
|
|
58
|
+
'input',
|
|
59
|
+
'label',
|
|
60
|
+
'legend',
|
|
61
|
+
'li',
|
|
62
|
+
'link',
|
|
63
|
+
'main',
|
|
64
|
+
'mark',
|
|
65
|
+
'menu',
|
|
66
|
+
'meta',
|
|
67
|
+
'meter',
|
|
68
|
+
'nav',
|
|
69
|
+
'ol',
|
|
70
|
+
'option',
|
|
71
|
+
'p',
|
|
72
|
+
'picture',
|
|
73
|
+
'pre',
|
|
74
|
+
'progress',
|
|
75
|
+
'q',
|
|
76
|
+
'script',
|
|
77
|
+
'section',
|
|
78
|
+
'select',
|
|
79
|
+
'small',
|
|
80
|
+
'source',
|
|
81
|
+
'span',
|
|
82
|
+
'strong',
|
|
83
|
+
'style',
|
|
84
|
+
'summary',
|
|
85
|
+
'svg',
|
|
86
|
+
'table',
|
|
87
|
+
'tbody',
|
|
88
|
+
'td',
|
|
89
|
+
'template',
|
|
90
|
+
'textarea',
|
|
91
|
+
'tfoot',
|
|
92
|
+
'th',
|
|
93
|
+
'thead',
|
|
94
|
+
'time',
|
|
95
|
+
'title',
|
|
96
|
+
'tr',
|
|
97
|
+
'u',
|
|
98
|
+
'ul',
|
|
99
|
+
'video',
|
|
100
|
+
]);
|
|
10
101
|
|
|
11
102
|
const collectFiles = (directory) => {
|
|
12
103
|
if (!fs.existsSync(directory)) {
|
|
@@ -37,6 +128,24 @@ const isCodeElementText = (content, index) => {
|
|
|
37
128
|
}
|
|
38
129
|
return /^<code(?:\s|>)/u.test(content.slice(tagStart, index));
|
|
39
130
|
};
|
|
131
|
+
const isJsxTagEnd = (content, index) => {
|
|
132
|
+
const tagStart = content.lastIndexOf('<', index);
|
|
133
|
+
if (tagStart === -1 || content.slice(tagStart + 1, index).includes('<')) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
const match = content
|
|
137
|
+
.slice(tagStart, index + 1)
|
|
138
|
+
.match(/^<\/?\s*([A-Za-z][\w:.-]*)\b[^<>]*>$/u);
|
|
139
|
+
if (!match) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
const [, tagName] = match;
|
|
143
|
+
return (
|
|
144
|
+
/^[A-Z]/u.test(tagName) ||
|
|
145
|
+
tagName.includes('-') ||
|
|
146
|
+
jsxIntrinsicTags.has(tagName)
|
|
147
|
+
);
|
|
148
|
+
};
|
|
40
149
|
const isIgnoredLine = (content, index) => {
|
|
41
150
|
const lineStart = content.lastIndexOf('\n', index) + 1;
|
|
42
151
|
const lineEnd = content.indexOf('\n', index);
|
|
@@ -54,20 +163,23 @@ const violations = [];
|
|
|
54
163
|
for (const filePath of scanRoots.flatMap(collectFiles)) {
|
|
55
164
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
56
165
|
for (const match of content.matchAll(visibleAttributePattern)) {
|
|
166
|
+
const [, visibleText] = match;
|
|
57
167
|
if (!isIgnoredLine(content, match.index ?? 0)) {
|
|
58
168
|
violations.push({
|
|
59
169
|
filePath,
|
|
60
170
|
line: lineNumberForIndex(content, match.index ?? 0),
|
|
61
|
-
text:
|
|
171
|
+
text: visibleText.trim(),
|
|
62
172
|
});
|
|
63
173
|
}
|
|
64
174
|
}
|
|
65
175
|
|
|
66
176
|
for (const match of content.matchAll(jsxTextPattern)) {
|
|
67
|
-
const
|
|
177
|
+
const [, jsxText] = match;
|
|
178
|
+
const text = jsxText.replaceAll(/\s+/gu, ' ').trim();
|
|
68
179
|
if (
|
|
69
180
|
text &&
|
|
70
181
|
!isIgnoredLine(content, match.index ?? 0) &&
|
|
182
|
+
isJsxTagEnd(content, match.index ?? 0) &&
|
|
71
183
|
!isCodeElementText(content, match.index ?? 0)
|
|
72
184
|
) {
|
|
73
185
|
violations.push({
|
|
@@ -422,11 +422,19 @@ if (readPnpmConfig('minimumReleaseAgeIgnoreMissingTime') !== false) {
|
|
|
422
422
|
console.error('pnpm minimumReleaseAgeIgnoreMissingTime must be false');
|
|
423
423
|
process.exit(1);
|
|
424
424
|
}
|
|
425
|
+
const expectedMinimumReleaseAgeExcludes = [
|
|
426
|
+
'@bleedingdev/modern-js-*',
|
|
427
|
+
'@tanstack/react-router',
|
|
428
|
+
'@tanstack/router-core',
|
|
429
|
+
'@typescript/native-preview',
|
|
430
|
+
'@typescript/native-preview-*',
|
|
431
|
+
'@types/react',
|
|
432
|
+
];
|
|
425
433
|
if (
|
|
426
434
|
JSON.stringify(readPnpmConfig('minimumReleaseAgeExclude')) !==
|
|
427
|
-
JSON.stringify(
|
|
435
|
+
JSON.stringify(expectedMinimumReleaseAgeExcludes)
|
|
428
436
|
) {
|
|
429
|
-
console.error('pnpm minimumReleaseAgeExclude must allow
|
|
437
|
+
console.error('pnpm minimumReleaseAgeExclude must allow only approved latest-lane package cohorts');
|
|
430
438
|
process.exit(1);
|
|
431
439
|
}
|
|
432
440
|
if (readPnpmConfig('trustPolicy') !== 'no-downgrade') {
|
|
@@ -481,6 +489,11 @@ if (
|
|
|
481
489
|
process.exit(1);
|
|
482
490
|
}
|
|
483
491
|
|
|
492
|
+
if (packageJson.modernjs?.packageSource?.strategy !== packageSource.strategy) {
|
|
493
|
+
console.error('package.json package source strategy must match package source metadata');
|
|
494
|
+
process.exit(1);
|
|
495
|
+
}
|
|
496
|
+
|
|
484
497
|
if (packageSource.schemaVersion !== 1) {
|
|
485
498
|
console.error('Package source metadata must use schemaVersion 1');
|
|
486
499
|
process.exit(1);
|
|
@@ -496,21 +509,33 @@ if (packageSource.strategy !== 'workspace' && packageSource.strategy !== 'instal
|
|
|
496
509
|
process.exit(1);
|
|
497
510
|
}
|
|
498
511
|
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
'
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
'@modern-js/plugin-tanstack',
|
|
505
|
-
'@modern-js/plugin-bff',
|
|
506
|
-
'@modern-js/adapter-rstest',
|
|
507
|
-
];
|
|
512
|
+
const declaredModernPackages = packageSource.modernPackages?.packages;
|
|
513
|
+
if (!Array.isArray(declaredModernPackages) || declaredModernPackages.length === 0) {
|
|
514
|
+
console.error('Package source metadata must declare the generated Modern package cohort');
|
|
515
|
+
process.exit(1);
|
|
516
|
+
}
|
|
508
517
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
}
|
|
518
|
+
const invalidModernPackages = declaredModernPackages.filter(
|
|
519
|
+
(packageName) => typeof packageName !== 'string' || !packageName.startsWith('@modern-js/'),
|
|
520
|
+
);
|
|
521
|
+
if (invalidModernPackages.length > 0) {
|
|
522
|
+
console.error(`Package source metadata contains invalid Modern packages: ${invalidModernPackages.join(', ')}`);
|
|
523
|
+
process.exit(1);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const packageJsonModernDependencies = [
|
|
527
|
+
...new Set(
|
|
528
|
+
['dependencies', 'devDependencies']
|
|
529
|
+
.flatMap((section) => Object.keys(packageJson[section] ?? {}))
|
|
530
|
+
.filter((packageName) => packageName.startsWith('@modern-js/')),
|
|
531
|
+
),
|
|
532
|
+
];
|
|
533
|
+
const missingMetadataPackages = packageJsonModernDependencies.filter(
|
|
534
|
+
(packageName) => !declaredModernPackages.includes(packageName),
|
|
535
|
+
);
|
|
536
|
+
if (missingMetadataPackages.length > 0) {
|
|
537
|
+
console.error(`Package source metadata must include package.json Modern dependencies: ${missingMetadataPackages.join(', ')}`);
|
|
538
|
+
process.exit(1);
|
|
514
539
|
}
|
|
515
540
|
|
|
516
541
|
const expectedModernSpecifier = packageSource.modernPackages?.specifier;
|
|
@@ -535,32 +560,15 @@ const expectedModernDependency = (packageName) => {
|
|
|
535
560
|
: expectedModernSpecifier;
|
|
536
561
|
};
|
|
537
562
|
|
|
538
|
-
for (const
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
])
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
console.error(`Dependency ${packageName} must match package source metadata`);
|
|
548
|
-
process.exit(1);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
for (const packageName of [
|
|
553
|
-
'@modern-js/app-tools',
|
|
554
|
-
'@modern-js/adapter-rstest',
|
|
555
|
-
'@modern-js/tsconfig',
|
|
556
|
-
'@modern-js/plugin-bff',
|
|
557
|
-
]) {
|
|
558
|
-
if (
|
|
559
|
-
packageJson.devDependencies?.[packageName] &&
|
|
560
|
-
packageJson.devDependencies[packageName] !== expectedModernDependency(packageName)
|
|
561
|
-
) {
|
|
562
|
-
console.error(`Dev dependency ${packageName} must match package source metadata`);
|
|
563
|
-
process.exit(1);
|
|
563
|
+
for (const section of ['dependencies', 'devDependencies']) {
|
|
564
|
+
for (const packageName of declaredModernPackages) {
|
|
565
|
+
if (
|
|
566
|
+
packageJson[section]?.[packageName] &&
|
|
567
|
+
packageJson[section][packageName] !== expectedModernDependency(packageName)
|
|
568
|
+
) {
|
|
569
|
+
console.error(`${section}.${packageName} must match package source metadata`);
|
|
570
|
+
process.exit(1);
|
|
571
|
+
}
|
|
564
572
|
}
|
|
565
573
|
}
|
|
566
574
|
|
|
@@ -89,6 +89,7 @@ describe('generated UltraModern contract', () => {
|
|
|
89
89
|
modernjs?: {
|
|
90
90
|
packageSource?: {
|
|
91
91
|
config?: string;
|
|
92
|
+
strategy?: string;
|
|
92
93
|
};
|
|
93
94
|
preset?: string;
|
|
94
95
|
};
|
|
@@ -105,15 +106,20 @@ describe('generated UltraModern contract', () => {
|
|
|
105
106
|
expect(packageJson.modernjs?.packageSource?.config).toBe(
|
|
106
107
|
'./.modernjs/ultramodern-package-source.json',
|
|
107
108
|
);
|
|
108
|
-
expect(packageSource
|
|
109
|
-
|
|
110
|
-
'@modern-js/runtime',
|
|
111
|
-
);
|
|
112
|
-
expect(packageSource.modernPackages?.packages).toContain(
|
|
113
|
-
'@modern-js/app-tools',
|
|
109
|
+
expect(packageJson.modernjs?.packageSource?.strategy).toBe(
|
|
110
|
+
packageSource.strategy,
|
|
114
111
|
);
|
|
115
|
-
expect(packageSource.
|
|
116
|
-
|
|
112
|
+
expect(packageSource.strategy).toMatch(/^(workspace|install)$/u);
|
|
113
|
+
const generatedModernDependencies = [
|
|
114
|
+
...new Set(
|
|
115
|
+
(['dependencies', 'devDependencies'] as const).flatMap(section =>
|
|
116
|
+
Object.keys(packageJson[section] ?? {}),
|
|
117
|
+
),
|
|
118
|
+
),
|
|
119
|
+
].filter(packageName => packageName.startsWith('@modern-js/'));
|
|
120
|
+
expect(packageSource.modernPackages?.packages?.length).toBeGreaterThan(0);
|
|
121
|
+
expect(packageSource.modernPackages?.packages).toEqual(
|
|
122
|
+
expect.arrayContaining(generatedModernDependencies),
|
|
117
123
|
);
|
|
118
124
|
expect(packageSource.modernPackages?.specifier).toBeTruthy();
|
|
119
125
|
expect(
|
|
@@ -19,9 +19,18 @@ pnpm dlx @bleedingdev/modern-js-create payments --vertical
|
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
Each added vertical owns its UI/routes, browser-safe Module Federation exposes,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
stay out of browser exposes.
|
|
22
|
+
private-first route metadata, localized URLs, public-route opt-ins, CSS prefix,
|
|
23
|
+
Effect BFF handlers, local API contract, and typed client surface. Server
|
|
24
|
+
handlers and Effect client/contract modules stay out of browser exposes.
|
|
25
|
+
|
|
26
|
+
## Private-First Public Surfaces
|
|
27
|
+
|
|
28
|
+
Generated app routes are private and non-indexable by default. Private app,
|
|
29
|
+
auth, tenant, dashboard, and internal routes publish no discovery output unless
|
|
30
|
+
route metadata explicitly marks them `public && indexable`. The default scaffold
|
|
31
|
+
therefore emits only a disallowing `robots.txt`; sitemap, web manifest,
|
|
32
|
+
`llms.txt`, API catalog, security.txt, and JSON-LD output stay omitted until a
|
|
33
|
+
safe public route or public docs/help/product surface exists.
|
|
25
34
|
|
|
26
35
|
Run the scaffold validator before adding business code and after every
|
|
27
36
|
`--vertical` mutation:
|
|
@@ -8,6 +8,11 @@ minimumReleaseAgeStrict: true
|
|
|
8
8
|
minimumReleaseAgeIgnoreMissingTime: false
|
|
9
9
|
minimumReleaseAgeExclude:
|
|
10
10
|
- '@bleedingdev/modern-js-*'
|
|
11
|
+
- '@tanstack/react-router'
|
|
12
|
+
- '@tanstack/router-core'
|
|
13
|
+
- '@typescript/native-preview'
|
|
14
|
+
- '@typescript/native-preview-*'
|
|
15
|
+
- '@types/react'
|
|
11
16
|
trustPolicy: no-downgrade
|
|
12
17
|
trustPolicyIgnoreAfter: 1440
|
|
13
18
|
blockExoticSubdeps: true
|
|
@@ -22,7 +27,7 @@ peerDependencyRules:
|
|
|
22
27
|
typescript: '>=6.0.0'
|
|
23
28
|
|
|
24
29
|
overrides:
|
|
25
|
-
'@tanstack/react-router': 1.170.
|
|
30
|
+
'@tanstack/react-router': 1.170.15
|
|
26
31
|
node-fetch: '^3.3.2'
|
|
27
32
|
|
|
28
33
|
allowBuilds:
|
|
@@ -122,6 +122,12 @@ function porcelainStatus() {
|
|
|
122
122
|
return run('git', ['status', '--porcelain'], { timeout: 30000 });
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
function commitInstallerChanges(message) {
|
|
126
|
+
run('git', ['commit', '--no-verify', '-m', message], {
|
|
127
|
+
timeout: 120000,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
125
131
|
function ensureGitRepository() {
|
|
126
132
|
if (!isGitWorkTree()) {
|
|
127
133
|
if (checkOnly) {
|
|
@@ -139,9 +145,7 @@ function ensureGitRepository() {
|
|
|
139
145
|
}
|
|
140
146
|
log('creating initial workspace commit before adding reference subtrees');
|
|
141
147
|
run('git', ['add', '-A'], { timeout: 30000 });
|
|
142
|
-
|
|
143
|
-
timeout: 120000,
|
|
144
|
-
});
|
|
148
|
+
commitInstallerChanges('Initialize UltraModern workspace');
|
|
145
149
|
return true;
|
|
146
150
|
}
|
|
147
151
|
|
|
@@ -311,9 +315,7 @@ function commitManifestIfChanged() {
|
|
|
311
315
|
return;
|
|
312
316
|
}
|
|
313
317
|
run('git', ['add', manifestPath], { timeout: 30000 });
|
|
314
|
-
|
|
315
|
-
timeout: 120000,
|
|
316
|
-
});
|
|
318
|
+
commitInstallerChanges('Record agent reference repo manifest');
|
|
317
319
|
}
|
|
318
320
|
|
|
319
321
|
function main() {
|