@bleedingdev/modern-js-create 3.2.0-ultramodern.108 → 3.2.0-ultramodern.110

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 (44) hide show
  1. package/dist/cjs/index.cjs +1040 -0
  2. package/dist/cjs/locale/en.cjs +97 -0
  3. package/dist/cjs/locale/index.cjs +50 -0
  4. package/dist/cjs/locale/zh.cjs +97 -0
  5. package/dist/cjs/ultramodern-checks/cli/i18n-check.cjs +73 -0
  6. package/dist/cjs/ultramodern-checks/cli/oxlint.cjs +174 -0
  7. package/dist/cjs/ultramodern-checks/cli/workspace-source-check.cjs +179 -0
  8. package/dist/cjs/ultramodern-checks/index.cjs +58 -0
  9. package/dist/cjs/ultramodern-checks/oxlint-plugin.cjs +354 -0
  10. package/dist/cjs/ultramodern-package-source.cjs +133 -0
  11. package/dist/cjs/ultramodern-workspace.cjs +5616 -0
  12. package/dist/esm/index.js +1002 -0
  13. package/dist/esm/locale/en.js +59 -0
  14. package/dist/esm/locale/index.js +9 -0
  15. package/dist/esm/locale/zh.js +59 -0
  16. package/dist/esm/ultramodern-checks/cli/i18n-check.js +26 -0
  17. package/dist/esm/ultramodern-checks/cli/oxlint.js +118 -0
  18. package/dist/esm/ultramodern-checks/cli/workspace-source-check.js +124 -0
  19. package/dist/esm/ultramodern-checks/index.js +3 -0
  20. package/dist/esm/ultramodern-checks/oxlint-plugin.js +316 -0
  21. package/dist/esm/ultramodern-package-source.js +61 -0
  22. package/dist/esm/ultramodern-workspace.js +5554 -0
  23. package/dist/esm-node/index.js +1003 -0
  24. package/dist/esm-node/locale/en.js +60 -0
  25. package/dist/esm-node/locale/index.js +10 -0
  26. package/dist/esm-node/locale/zh.js +60 -0
  27. package/dist/esm-node/ultramodern-checks/cli/i18n-check.js +27 -0
  28. package/dist/esm-node/ultramodern-checks/cli/oxlint.js +119 -0
  29. package/dist/esm-node/ultramodern-checks/cli/workspace-source-check.js +125 -0
  30. package/dist/esm-node/ultramodern-checks/index.js +4 -0
  31. package/dist/esm-node/ultramodern-checks/oxlint-plugin.js +317 -0
  32. package/dist/esm-node/ultramodern-package-source.js +62 -0
  33. package/dist/{index.js → esm-node/ultramodern-workspace.js} +31 -1793
  34. package/dist/types/ultramodern-checks/cli/i18n-check.d.ts +9 -0
  35. package/dist/types/ultramodern-checks/cli/oxlint.d.ts +22 -0
  36. package/dist/types/ultramodern-checks/cli/workspace-source-check.d.ts +8 -0
  37. package/dist/types/ultramodern-checks/index.d.ts +3 -0
  38. package/dist/types/ultramodern-checks/oxlint-plugin.d.ts +63 -0
  39. package/dist/types/ultramodern-package-source.d.ts +2 -2
  40. package/package.json +49 -8
  41. package/template/package.json.handlebars +7 -5
  42. package/template/scripts/check-i18n-strings.mjs +2 -205
  43. package/template/scripts/validate-ultramodern.mjs.handlebars +27 -9
  44. package/template/tests/ultramodern.contract.test.ts.handlebars +19 -0
@@ -1,622 +1,9 @@
1
- import { execFileSync } from "node:child_process";
1
+ import "node:module";
2
2
  import node_crypto from "node:crypto";
3
3
  import node_fs from "node:fs";
4
4
  import node_path from "node:path";
5
- import node_readline from "node:readline";
6
5
  import { fileURLToPath } from "node:url";
7
- import "node:module";
8
- class I18CLILanguageDetector {
9
- formatShellLocale(rawLC) {
10
- if (!rawLC) return '';
11
- const LCs = rawLC.split(':');
12
- const LC = LCs[0].split('.')[0].split('_')[0].split('-')[0];
13
- if ('C' === LC) return '';
14
- return LC;
15
- }
16
- detect() {
17
- const env = globalThis.process?.env;
18
- const shellLocale = env?.LC_ALL ?? env?.LC_MESSAGES ?? env?.LANG ?? env?.LANGUAGE ?? Intl.DateTimeFormat().resolvedOptions().locale;
19
- return this.formatShellLocale(shellLocale);
20
- }
21
- }
22
- function getLocaleLanguage() {
23
- const detector = new I18CLILanguageDetector();
24
- return detector.detect();
25
- }
26
- var isArray = Array.isArray;
27
- const lodash_es_isArray = isArray;
28
- var freeGlobal = 'object' == typeof global && global && global.Object === Object && global;
29
- const _freeGlobal = freeGlobal;
30
- var freeSelf = 'object' == typeof self && self && self.Object === Object && self;
31
- var _root_root = _freeGlobal || freeSelf || Function('return this')();
32
- const _root = _root_root;
33
- var Symbol = _root.Symbol;
34
- const _Symbol = Symbol;
35
- var objectProto = Object.prototype;
36
- var _getRawTag_hasOwnProperty = objectProto.hasOwnProperty;
37
- var nativeObjectToString = objectProto.toString;
38
- var symToStringTag = _Symbol ? _Symbol.toStringTag : void 0;
39
- function getRawTag(value) {
40
- var isOwn = _getRawTag_hasOwnProperty.call(value, symToStringTag), tag = value[symToStringTag];
41
- try {
42
- value[symToStringTag] = void 0;
43
- var unmasked = true;
44
- } catch (e) {}
45
- var result = nativeObjectToString.call(value);
46
- if (unmasked) if (isOwn) value[symToStringTag] = tag;
47
- else delete value[symToStringTag];
48
- return result;
49
- }
50
- const _getRawTag = getRawTag;
51
- var _objectToString_objectProto = Object.prototype;
52
- var _objectToString_nativeObjectToString = _objectToString_objectProto.toString;
53
- function objectToString(value) {
54
- return _objectToString_nativeObjectToString.call(value);
55
- }
56
- const _objectToString = objectToString;
57
- var nullTag = '[object Null]', undefinedTag = '[object Undefined]';
58
- var _baseGetTag_symToStringTag = _Symbol ? _Symbol.toStringTag : void 0;
59
- function baseGetTag(value) {
60
- if (null == value) return void 0 === value ? undefinedTag : nullTag;
61
- return _baseGetTag_symToStringTag && _baseGetTag_symToStringTag in Object(value) ? _getRawTag(value) : _objectToString(value);
62
- }
63
- const _baseGetTag = baseGetTag;
64
- function isObjectLike(value) {
65
- return null != value && 'object' == typeof value;
66
- }
67
- const lodash_es_isObjectLike = isObjectLike;
68
- var symbolTag = '[object Symbol]';
69
- function isSymbol(value) {
70
- return 'symbol' == typeof value || lodash_es_isObjectLike(value) && _baseGetTag(value) == symbolTag;
71
- }
72
- const lodash_es_isSymbol = isSymbol;
73
- var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, reIsPlainProp = /^\w*$/;
74
- function isKey(value, object) {
75
- if (lodash_es_isArray(value)) return false;
76
- var type = typeof value;
77
- if ('number' == type || 'symbol' == type || 'boolean' == type || null == value || lodash_es_isSymbol(value)) return true;
78
- return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || null != object && value in Object(object);
79
- }
80
- const _isKey = isKey;
81
- function isObject(value) {
82
- var type = typeof value;
83
- return null != value && ('object' == type || 'function' == type);
84
- }
85
- const lodash_es_isObject = isObject;
86
- var asyncTag = '[object AsyncFunction]', funcTag = '[object Function]', genTag = '[object GeneratorFunction]', proxyTag = '[object Proxy]';
87
- function isFunction(value) {
88
- if (!lodash_es_isObject(value)) return false;
89
- var tag = _baseGetTag(value);
90
- return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
91
- }
92
- const lodash_es_isFunction = isFunction;
93
- var coreJsData = _root["__core-js_shared__"];
94
- const _coreJsData = coreJsData;
95
- var maskSrcKey = function() {
96
- var uid = /[^.]+$/.exec(_coreJsData && _coreJsData.keys && _coreJsData.keys.IE_PROTO || '');
97
- return uid ? 'Symbol(src)_1.' + uid : '';
98
- }();
99
- function isMasked(func) {
100
- return !!maskSrcKey && maskSrcKey in func;
101
- }
102
- const _isMasked = isMasked;
103
- var funcProto = Function.prototype;
104
- var funcToString = funcProto.toString;
105
- function toSource(func) {
106
- if (null != func) {
107
- try {
108
- return funcToString.call(func);
109
- } catch (e) {}
110
- try {
111
- return func + '';
112
- } catch (e) {}
113
- }
114
- return '';
115
- }
116
- const _toSource = toSource;
117
- var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
118
- var reIsHostCtor = /^\[object .+?Constructor\]$/;
119
- var _baseIsNative_funcProto = Function.prototype, _baseIsNative_objectProto = Object.prototype;
120
- var _baseIsNative_funcToString = _baseIsNative_funcProto.toString;
121
- var _baseIsNative_hasOwnProperty = _baseIsNative_objectProto.hasOwnProperty;
122
- var reIsNative = RegExp('^' + _baseIsNative_funcToString.call(_baseIsNative_hasOwnProperty).replace(reRegExpChar, '\\$&').replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$');
123
- function baseIsNative(value) {
124
- if (!lodash_es_isObject(value) || _isMasked(value)) return false;
125
- var pattern = lodash_es_isFunction(value) ? reIsNative : reIsHostCtor;
126
- return pattern.test(_toSource(value));
127
- }
128
- const _baseIsNative = baseIsNative;
129
- function getValue(object, key) {
130
- return null == object ? void 0 : object[key];
131
- }
132
- const _getValue = getValue;
133
- function getNative(object, key) {
134
- var value = _getValue(object, key);
135
- return _baseIsNative(value) ? value : void 0;
136
- }
137
- const _getNative = getNative;
138
- var nativeCreate = _getNative(Object, 'create');
139
- const _nativeCreate = nativeCreate;
140
- function hashClear() {
141
- this.__data__ = _nativeCreate ? _nativeCreate(null) : {};
142
- this.size = 0;
143
- }
144
- const _hashClear = hashClear;
145
- function hashDelete(key) {
146
- var result = this.has(key) && delete this.__data__[key];
147
- this.size -= result ? 1 : 0;
148
- return result;
149
- }
150
- const _hashDelete = hashDelete;
151
- var HASH_UNDEFINED = '__lodash_hash_undefined__';
152
- var _hashGet_objectProto = Object.prototype;
153
- var _hashGet_hasOwnProperty = _hashGet_objectProto.hasOwnProperty;
154
- function hashGet(key) {
155
- var data = this.__data__;
156
- if (_nativeCreate) {
157
- var result = data[key];
158
- return result === HASH_UNDEFINED ? void 0 : result;
159
- }
160
- return _hashGet_hasOwnProperty.call(data, key) ? data[key] : void 0;
161
- }
162
- const _hashGet = hashGet;
163
- var _hashHas_objectProto = Object.prototype;
164
- var _hashHas_hasOwnProperty = _hashHas_objectProto.hasOwnProperty;
165
- function hashHas(key) {
166
- var data = this.__data__;
167
- return _nativeCreate ? void 0 !== data[key] : _hashHas_hasOwnProperty.call(data, key);
168
- }
169
- const _hashHas = hashHas;
170
- var _hashSet_HASH_UNDEFINED = '__lodash_hash_undefined__';
171
- function hashSet(key, value) {
172
- var data = this.__data__;
173
- this.size += this.has(key) ? 0 : 1;
174
- data[key] = _nativeCreate && void 0 === value ? _hashSet_HASH_UNDEFINED : value;
175
- return this;
176
- }
177
- const _hashSet = hashSet;
178
- function Hash(entries) {
179
- var index = -1, length = null == entries ? 0 : entries.length;
180
- this.clear();
181
- while(++index < length){
182
- var entry = entries[index];
183
- this.set(entry[0], entry[1]);
184
- }
185
- }
186
- Hash.prototype.clear = _hashClear;
187
- Hash.prototype['delete'] = _hashDelete;
188
- Hash.prototype.get = _hashGet;
189
- Hash.prototype.has = _hashHas;
190
- Hash.prototype.set = _hashSet;
191
- const _Hash = Hash;
192
- function listCacheClear() {
193
- this.__data__ = [];
194
- this.size = 0;
195
- }
196
- const _listCacheClear = listCacheClear;
197
- function eq(value, other) {
198
- return value === other || value !== value && other !== other;
199
- }
200
- const lodash_es_eq = eq;
201
- function assocIndexOf(array, key) {
202
- var length = array.length;
203
- while(length--)if (lodash_es_eq(array[length][0], key)) return length;
204
- return -1;
205
- }
206
- const _assocIndexOf = assocIndexOf;
207
- var arrayProto = Array.prototype;
208
- var splice = arrayProto.splice;
209
- function listCacheDelete(key) {
210
- var data = this.__data__, index = _assocIndexOf(data, key);
211
- if (index < 0) return false;
212
- var lastIndex = data.length - 1;
213
- if (index == lastIndex) data.pop();
214
- else splice.call(data, index, 1);
215
- --this.size;
216
- return true;
217
- }
218
- const _listCacheDelete = listCacheDelete;
219
- function listCacheGet(key) {
220
- var data = this.__data__, index = _assocIndexOf(data, key);
221
- return index < 0 ? void 0 : data[index][1];
222
- }
223
- const _listCacheGet = listCacheGet;
224
- function listCacheHas(key) {
225
- return _assocIndexOf(this.__data__, key) > -1;
226
- }
227
- const _listCacheHas = listCacheHas;
228
- function listCacheSet(key, value) {
229
- var data = this.__data__, index = _assocIndexOf(data, key);
230
- if (index < 0) {
231
- ++this.size;
232
- data.push([
233
- key,
234
- value
235
- ]);
236
- } else data[index][1] = value;
237
- return this;
238
- }
239
- const _listCacheSet = listCacheSet;
240
- function ListCache(entries) {
241
- var index = -1, length = null == entries ? 0 : entries.length;
242
- this.clear();
243
- while(++index < length){
244
- var entry = entries[index];
245
- this.set(entry[0], entry[1]);
246
- }
247
- }
248
- ListCache.prototype.clear = _listCacheClear;
249
- ListCache.prototype['delete'] = _listCacheDelete;
250
- ListCache.prototype.get = _listCacheGet;
251
- ListCache.prototype.has = _listCacheHas;
252
- ListCache.prototype.set = _listCacheSet;
253
- const _ListCache = ListCache;
254
- var _Map_Map = _getNative(_root, 'Map');
255
- const _Map = _Map_Map;
256
- function mapCacheClear() {
257
- this.size = 0;
258
- this.__data__ = {
259
- hash: new _Hash,
260
- map: new (_Map || _ListCache),
261
- string: new _Hash
262
- };
263
- }
264
- const _mapCacheClear = mapCacheClear;
265
- function isKeyable(value) {
266
- var type = typeof value;
267
- return 'string' == type || 'number' == type || 'symbol' == type || 'boolean' == type ? '__proto__' !== value : null === value;
268
- }
269
- const _isKeyable = isKeyable;
270
- function getMapData(map, key) {
271
- var data = map.__data__;
272
- return _isKeyable(key) ? data['string' == typeof key ? 'string' : 'hash'] : data.map;
273
- }
274
- const _getMapData = getMapData;
275
- function mapCacheDelete(key) {
276
- var result = _getMapData(this, key)['delete'](key);
277
- this.size -= result ? 1 : 0;
278
- return result;
279
- }
280
- const _mapCacheDelete = mapCacheDelete;
281
- function mapCacheGet(key) {
282
- return _getMapData(this, key).get(key);
283
- }
284
- const _mapCacheGet = mapCacheGet;
285
- function mapCacheHas(key) {
286
- return _getMapData(this, key).has(key);
287
- }
288
- const _mapCacheHas = mapCacheHas;
289
- function mapCacheSet(key, value) {
290
- var data = _getMapData(this, key), size = data.size;
291
- data.set(key, value);
292
- this.size += data.size == size ? 0 : 1;
293
- return this;
294
- }
295
- const _mapCacheSet = mapCacheSet;
296
- function MapCache(entries) {
297
- var index = -1, length = null == entries ? 0 : entries.length;
298
- this.clear();
299
- while(++index < length){
300
- var entry = entries[index];
301
- this.set(entry[0], entry[1]);
302
- }
303
- }
304
- MapCache.prototype.clear = _mapCacheClear;
305
- MapCache.prototype['delete'] = _mapCacheDelete;
306
- MapCache.prototype.get = _mapCacheGet;
307
- MapCache.prototype.has = _mapCacheHas;
308
- MapCache.prototype.set = _mapCacheSet;
309
- const _MapCache = MapCache;
310
- var FUNC_ERROR_TEXT = 'Expected a function';
311
- function memoize(func, resolver) {
312
- if ('function' != typeof func || null != resolver && 'function' != typeof resolver) throw new TypeError(FUNC_ERROR_TEXT);
313
- var memoized = function() {
314
- var args = arguments, key = resolver ? resolver.apply(this, args) : args[0], cache = memoized.cache;
315
- if (cache.has(key)) return cache.get(key);
316
- var result = func.apply(this, args);
317
- memoized.cache = cache.set(key, result) || cache;
318
- return result;
319
- };
320
- memoized.cache = new (memoize.Cache || _MapCache);
321
- return memoized;
322
- }
323
- memoize.Cache = _MapCache;
324
- const lodash_es_memoize = memoize;
325
- var MAX_MEMOIZE_SIZE = 500;
326
- function memoizeCapped(func) {
327
- var result = lodash_es_memoize(func, function(key) {
328
- if (cache.size === MAX_MEMOIZE_SIZE) cache.clear();
329
- return key;
330
- });
331
- var cache = result.cache;
332
- return result;
333
- }
334
- const _memoizeCapped = memoizeCapped;
335
- var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;
336
- var reEscapeChar = /\\(\\)?/g;
337
- var stringToPath = _memoizeCapped(function(string) {
338
- var result = [];
339
- if (46 === string.charCodeAt(0)) result.push('');
340
- string.replace(rePropName, function(match, number, quote, subString) {
341
- result.push(quote ? subString.replace(reEscapeChar, '$1') : number || match);
342
- });
343
- return result;
344
- });
345
- const _stringToPath = stringToPath;
346
- function arrayMap(array, iteratee) {
347
- var index = -1, length = null == array ? 0 : array.length, result = Array(length);
348
- while(++index < length)result[index] = iteratee(array[index], index, array);
349
- return result;
350
- }
351
- const _arrayMap = arrayMap;
352
- var INFINITY = 1 / 0;
353
- var symbolProto = _Symbol ? _Symbol.prototype : void 0, symbolToString = symbolProto ? symbolProto.toString : void 0;
354
- function baseToString(value) {
355
- if ('string' == typeof value) return value;
356
- if (lodash_es_isArray(value)) return _arrayMap(value, baseToString) + '';
357
- if (lodash_es_isSymbol(value)) return symbolToString ? symbolToString.call(value) : '';
358
- var result = value + '';
359
- return '0' == result && 1 / value == -INFINITY ? '-0' : result;
360
- }
361
- const _baseToString = baseToString;
362
- function toString_toString(value) {
363
- return null == value ? '' : _baseToString(value);
364
- }
365
- const lodash_es_toString = toString_toString;
366
- function castPath(value, object) {
367
- if (lodash_es_isArray(value)) return value;
368
- return _isKey(value, object) ? [
369
- value
370
- ] : _stringToPath(lodash_es_toString(value));
371
- }
372
- const _castPath = castPath;
373
- var _toKey_INFINITY = 1 / 0;
374
- function toKey(value) {
375
- if ('string' == typeof value || lodash_es_isSymbol(value)) return value;
376
- var result = value + '';
377
- return '0' == result && 1 / value == -_toKey_INFINITY ? '-0' : result;
378
- }
379
- const _toKey = toKey;
380
- function baseGet(object, path) {
381
- path = _castPath(path, object);
382
- var index = 0, length = path.length;
383
- while(null != object && index < length)object = object[_toKey(path[index++])];
384
- return index && index == length ? object : void 0;
385
- }
386
- const _baseGet = baseGet;
387
- function get(object, path, defaultValue) {
388
- var result = null == object ? void 0 : _baseGet(object, path);
389
- return void 0 === result ? defaultValue : result;
390
- }
391
- const lodash_es_get = get;
392
- var stringTag = '[object String]';
393
- function isString(value) {
394
- return 'string' == typeof value || !lodash_es_isArray(value) && lodash_es_isObjectLike(value) && _baseGetTag(value) == stringTag;
395
- }
396
- const lodash_es_isString = isString;
397
- function getObjKeyMap(obj, prefix = '') {
398
- const result = {};
399
- Object.keys(obj).forEach((key)=>{
400
- if (lodash_es_isString(obj[key])) result[key] = prefix ? `${prefix}.${key}` : key;
401
- else if (lodash_es_isObject(obj[key])) result[key] = getObjKeyMap(obj[key], prefix ? `${prefix}.${key}` : key);
402
- });
403
- return result;
404
- }
405
- class I18n {
406
- format(msg, vars) {
407
- return msg.replace(/\{(\w+)\}/g, (_match, capture)=>Object.prototype.hasOwnProperty.call(vars, capture) ? vars[capture] : capture);
408
- }
409
- getMessage(lang, key, vars, fallbackText) {
410
- const languages = Object.keys(this.languageMap);
411
- const resultLang = languages.find((l)=>l === lang);
412
- if (!resultLang && 0 === languages.length) return fallbackText || key;
413
- const model = this.languageMap[resultLang || 'en'];
414
- if (!model) return fallbackText || key;
415
- const message = lodash_es_get(model, key);
416
- const value = message || fallbackText || key;
417
- if ('string' == typeof value) return this.format(value, vars || {});
418
- throw new Error('key is not a string');
419
- }
420
- init(language, languageMap) {
421
- this.language = language || 'en';
422
- if (languageMap) this.languageMap = languageMap;
423
- return getObjKeyMap(this.languageMap[this.language]);
424
- }
425
- changeLanguage(config) {
426
- this.language = config.locale || 'en';
427
- }
428
- t(key, vars, fallbackText) {
429
- return this.getMessage(this.language, key, vars, fallbackText);
430
- }
431
- lang(lang) {
432
- return {
433
- t: (key, vars, fallbackText)=>this.getMessage(lang, key, vars, fallbackText)
434
- };
435
- }
436
- constructor(){
437
- this.language = 'en';
438
- this.languageMap = {};
439
- }
440
- }
441
- const EN_LOCALE = {
442
- prompt: {
443
- projectName: 'Please enter project name: '
444
- },
445
- error: {
446
- projectNameEmpty: 'Error: Project name cannot be empty',
447
- directoryExists: 'Error: Directory "{projectName}" already exists and is not empty',
448
- invalidRouter: 'Error: Unsupported router "{router}". Use "react-router" or "tanstack".',
449
- invalidBffRuntime: 'Error: Unsupported BFF runtime "{runtime}". Use "hono" or "effect".',
450
- createFailed: 'Error creating project:'
451
- },
452
- message: {
453
- welcome: '🚀 Welcome to UltraModern.js',
454
- success: '✨ Created successfully!',
455
- nextSteps: '📋 Next steps:',
456
- step1: 'cd {projectName}',
457
- step2: 'pnpm install',
458
- step3: 'pnpm dev'
459
- },
460
- help: {
461
- title: '🚀 UltraModern.js Project Creator',
462
- description: 'Create a new UltraModern.js app with TanStack Router and Effect BFF by default',
463
- usage: '📖 Usage:',
464
- usageExample: ' pnpm dlx @bleedingdev/modern-js-create [project-name] [options]',
465
- options: '⚙️ Options:',
466
- optionHelp: ' -h, --help Display this help message',
467
- optionVersion: ' -v, --version Display version information',
468
- optionLang: ' -l, --lang Set the language (zh or en)',
469
- optionRouter: ' -r, --router Select router framework (tanstack default; react-router is compatibility mode)',
470
- optionBff: ' --bff Keep Effect BFF enabled (default for UltraModern apps)',
471
- optionBffRuntime: ' --bff-runtime Select BFF runtime (hono or effect)',
472
- optionTailwind: ' --no-tailwind Disable default Tailwind CSS v4 scaffold',
473
- optionWorkspace: ' --workspace Use workspace protocol for @modern-js dependencies (for local monorepo testing)',
474
- optionUltramodernWorkspace: ' --ultramodern-workspace Generate an UltraModern SuperApp workspace (default is a full UltraModern single app)',
475
- optionUltramodernPackageSource: ' --ultramodern-package-source Select UltraModern package source (workspace or install; BleedingDev defaults to install aliases)',
476
- optionUltramodernPackageScope: ' --ultramodern-package-scope Publish scope for npm alias installs (for example bleedingdev)',
477
- optionUltramodernPackageNamePrefix: ' --ultramodern-package-name-prefix Prefix for npm alias package names (default: modern-js-)',
478
- optionVertical: ' --vertical Mutate the current existing UltraModern workspace and wire a MicroVertical named <project-name>',
479
- optionSub: ' -s, --sub Mark as a subproject (package in monorepo)',
480
- examples: '💡 Examples:',
481
- example1: ' pnpm dlx @bleedingdev/modern-js-create my-app',
482
- example2: ' pnpm dlx @bleedingdev/modern-js-create my-app --lang zh',
483
- example3: ' pnpm dlx @bleedingdev/modern-js-create my-app --sub',
484
- example4: ' pnpm dlx @bleedingdev/modern-js-create --help',
485
- example5: ' pnpm dlx @bleedingdev/modern-js-create .',
486
- example6: ' pnpm dlx @bleedingdev/modern-js-create my-app --router react-router --no-tailwind',
487
- example7: ' pnpm dlx @bleedingdev/modern-js-create my-app --bff-runtime hono',
488
- example8: ' pnpm dlx @bleedingdev/modern-js-create my-app --workspace',
489
- example9: ' pnpm dlx @bleedingdev/modern-js-create my-super-app --ultramodern-workspace',
490
- example10: ' pnpm dlx @bleedingdev/modern-js-create my-app --no-tailwind',
491
- example11: ' pnpm dlx @bleedingdev/modern-js-create my-app --router react-router # compatibility mode',
492
- example12: ' pnpm dlx @bleedingdev/modern-js-create catalog --vertical',
493
- moreInfo: '📚 Learn more: https://modernjs.dev'
494
- },
495
- version: {
496
- message: '@bleedingdev/modern-js-create version: {version}'
497
- }
498
- };
499
- const ZH_LOCALE = {
500
- prompt: {
501
- projectName: '请输入项目名称: '
502
- },
503
- error: {
504
- projectNameEmpty: '错误: 项目名称不能为空',
505
- directoryExists: '错误: 目录 "{projectName}" 已存在且不为空',
506
- invalidRouter: '错误: 不支持的路由器 "{router}",请使用 "react-router" 或 "tanstack"',
507
- invalidBffRuntime: '错误: 不支持的 BFF 运行时 "{runtime}",请使用 "hono" 或 "effect"',
508
- createFailed: '创建项目时出错:'
509
- },
510
- message: {
511
- welcome: '🚀 欢迎使用 UltraModern.js',
512
- success: '✨ 创建成功!',
513
- nextSteps: '📋 下一步:',
514
- step1: 'cd {projectName}',
515
- step2: 'pnpm install',
516
- step3: 'pnpm dev'
517
- },
518
- help: {
519
- title: '🚀 UltraModern.js 项目创建工具',
520
- description: '创建默认包含 TanStack Router 和 Effect BFF 的 UltraModern.js 应用',
521
- usage: '📖 用法:',
522
- usageExample: ' pnpm dlx @bleedingdev/modern-js-create [项目名称] [选项]',
523
- options: '⚙️ 选项:',
524
- optionHelp: ' -h, --help 显示帮助信息',
525
- optionVersion: ' -v, --version 显示版本信息',
526
- optionLang: ' -l, --lang 设置语言 (zh 或 en)',
527
- optionRouter: ' -r, --router 选择路由框架(默认 tanstack;react-router 为兼容模式)',
528
- optionBff: ' --bff 保持启用 Effect BFF(UltraModern 应用默认值)',
529
- optionBffRuntime: ' --bff-runtime 选择 BFF 运行时(hono 或 effect)',
530
- optionTailwind: ' --no-tailwind 禁用默认 Tailwind CSS v4 模板',
531
- optionWorkspace: ' --workspace 对 @modern-js 依赖使用 workspace 协议(用于本地 monorepo 联调)',
532
- optionUltramodernWorkspace: ' --ultramodern-workspace 生成 UltraModern SuperApp 工作区(默认创建完整 UltraModern 单应用)',
533
- optionUltramodernPackageSource: ' --ultramodern-package-source 选择 UltraModern 依赖来源(workspace 或 install;BleedingDev 默认使用 install alias)',
534
- optionUltramodernPackageScope: ' --ultramodern-package-scope npm alias 安装使用的发布 scope(例如 bleedingdev)',
535
- optionUltramodernPackageNamePrefix: ' --ultramodern-package-name-prefix npm alias 包名前缀(默认:modern-js-)',
536
- optionVertical: ' --vertical 修改当前已有的 UltraModern 工作区,并接入名为 <项目名称> 的 MicroVertical',
537
- optionSub: ' -s, --sub 标记为子项目(monorepo 中的子包)',
538
- examples: '💡 示例:',
539
- example1: ' pnpm dlx @bleedingdev/modern-js-create my-app',
540
- example2: ' pnpm dlx @bleedingdev/modern-js-create my-app --lang zh',
541
- example3: ' pnpm dlx @bleedingdev/modern-js-create my-app --sub',
542
- example4: ' pnpm dlx @bleedingdev/modern-js-create --help',
543
- example5: ' pnpm dlx @bleedingdev/modern-js-create .',
544
- example6: ' pnpm dlx @bleedingdev/modern-js-create my-app --router react-router --no-tailwind',
545
- example7: ' pnpm dlx @bleedingdev/modern-js-create my-app --bff-runtime hono',
546
- example8: ' pnpm dlx @bleedingdev/modern-js-create my-app --workspace',
547
- example9: ' pnpm dlx @bleedingdev/modern-js-create my-super-app --ultramodern-workspace',
548
- example10: ' pnpm dlx @bleedingdev/modern-js-create my-app --no-tailwind',
549
- example11: ' pnpm dlx @bleedingdev/modern-js-create my-app --router react-router # 兼容模式',
550
- example12: ' pnpm dlx @bleedingdev/modern-js-create catalog --vertical',
551
- moreInfo: '📚 更多信息: https://modernjs.dev'
552
- },
553
- version: {
554
- message: '@bleedingdev/modern-js-create 版本: {version}'
555
- }
556
- };
557
- const i18n = new I18n();
558
- const localeKeys = i18n.init('en', {
559
- zh: ZH_LOCALE,
560
- en: EN_LOCALE
561
- });
562
- const WORKSPACE_PACKAGE_VERSION = 'workspace:*';
563
- const BLEEDINGDEV_CREATE_PACKAGE = '@bleedingdev/modern-js-create';
564
- const BLEEDINGDEV_PACKAGE_SCOPE = 'bleedingdev';
565
- const BLEEDINGDEV_PACKAGE_NAME_PREFIX = 'modern-js-';
566
- const BLEEDINGDEV_FRAMEWORK_VERSION_ENV = 'MODERN_CREATE_ULTRAMODERN_FRAMEWORK_VERSION';
567
- const ULTRAMODERN_SINGLE_APP_MODERN_PACKAGES = [
568
- '@modern-js/runtime',
569
- '@modern-js/app-tools',
570
- '@modern-js/tsconfig',
571
- '@modern-js/plugin-i18n',
572
- '@modern-js/plugin-tanstack',
573
- '@modern-js/plugin-bff',
574
- '@modern-js/adapter-rstest'
575
- ];
576
- const ULTRAMODERN_WORKSPACE_MODERN_PACKAGES = [
577
- '@modern-js/app-tools',
578
- '@modern-js/plugin-bff',
579
- '@modern-js/plugin-i18n',
580
- '@modern-js/plugin-tanstack',
581
- '@modern-js/runtime'
582
- ];
583
- function modernPackageVersion(packageSource) {
584
- return 'install' === packageSource.strategy ? packageSource.modernPackageVersion : WORKSPACE_PACKAGE_VERSION;
585
- }
586
- function modernAliasPackageName(packageName, packageSource) {
587
- if (!packageSource.aliasScope) return packageName;
588
- const scope = packageSource.aliasScope.replace(/^@/, '');
589
- const unscopedName = packageName.split('/').at(-1);
590
- return `@${scope}/${packageSource.aliasPackageNamePrefix ?? ''}${unscopedName}`;
591
- }
592
- function modernPackageSpecifier(packageName, packageSource) {
593
- if ('install' !== packageSource.strategy) return WORKSPACE_PACKAGE_VERSION;
594
- if (!packageSource.aliasScope) return packageSource.modernPackageVersion;
595
- return `npm:${modernAliasPackageName(packageName, packageSource)}@${packageSource.modernPackageVersion}`;
596
- }
597
- function modernPackageAliases(packageNames, packageSource) {
598
- if (!packageSource.aliasScope) return;
599
- return Object.fromEntries(packageNames.map((packageName)=>[
600
- packageName,
601
- modernAliasPackageName(packageName, packageSource)
602
- ]));
603
- }
604
- function createModernPackagesMetadata(packageNames, packageSource, options = {}) {
605
- const includeAliases = options.includeAliases ?? Boolean(packageSource.aliasScope);
606
- const aliases = includeAliases ? modernPackageAliases(packageNames, packageSource) : void 0;
607
- return {
608
- packages: [
609
- ...packageNames
610
- ],
611
- specifier: modernPackageVersion(packageSource),
612
- ...packageSource.registry ? {
613
- registry: packageSource.registry
614
- } : {},
615
- ...aliases ? {
616
- aliases
617
- } : {}
618
- };
619
- }
6
+ import { BLEEDINGDEV_PACKAGE_NAME_PREFIX, BLEEDINGDEV_PACKAGE_SCOPE, ULTRAMODERN_WORKSPACE_MODERN_PACKAGES, WORKSPACE_PACKAGE_VERSION, createModernPackagesMetadata, modernPackageSpecifier, modernPackageVersion } from "./ultramodern-package-source.js";
620
7
  const ultramodern_workspace_dirname = node_path.dirname(fileURLToPath(import.meta.url));
621
8
  const workspaceTemplateDir = node_path.resolve(ultramodern_workspace_dirname, '..', 'template-workspace');
622
9
  const TANSTACK_ROUTER_VERSION = '1.170.15';
@@ -1092,6 +479,7 @@ function createRootPackageJson(scope, packageSource, remotes = []) {
1092
479
  },
1093
480
  devDependencies: {
1094
481
  '@effect/tsgo': EFFECT_TSGO_VERSION,
482
+ '@modern-js/create': modernPackageSpecifier('@modern-js/create', packageSource),
1095
483
  "@typescript/native-preview": TYPESCRIPT_NATIVE_PREVIEW_VERSION,
1096
484
  lefthook: LEFTHOOK_VERSION,
1097
485
  oxlint: OXLINT_VERSION,
@@ -4854,189 +4242,14 @@ for (const appDir of appDirs) {
4854
4242
  }
4855
4243
  function createWorkspaceI18nBoundaryValidationScript() {
4856
4244
  return `#!/usr/bin/env node
4857
- import fs from 'node:fs';
4858
4245
  import path from 'node:path';
4246
+ import { runWorkspaceSourceCheck } from '@modern-js/create/ultramodern-checks';
4859
4247
 
4860
4248
  const root = path.resolve(import.meta.dirname, '..');
4861
- const sourceRoots = ['apps', 'verticals'];
4862
- const languageConditionalPattern =
4863
- /\\b(language|locale|lng|currentLanguage)\\s*={0,2}={1,2}\\s*['"][a-z-]+['"]\\s*\\?\\s*([^:;\\n]+)\\s*:\\s*([^;\\n})]+)/gu;
4864
- const allowedLanguageConditionalBranches = new Set([
4865
- "'page'",
4866
- '"page"',
4867
- 'undefined',
4868
- 'null',
4869
- 'true',
4870
- 'false',
4871
- ]);
4872
- const visibleCopyAttributes = new Set([
4873
- 'alt',
4874
- 'aria-label',
4875
- 'label',
4876
- 'placeholder',
4877
- 'title',
4878
- ]);
4879
-
4880
- const fail = (message) => {
4881
- throw new Error(message);
4882
- };
4883
-
4884
- const walk = (directory, files = []) => {
4885
- if (!fs.existsSync(directory)) {
4886
- return files;
4887
- }
4888
- for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
4889
- if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === '.output') {
4890
- continue;
4891
- }
4892
- const entryPath = path.join(directory, entry.name);
4893
- if (entry.isDirectory()) {
4894
- walk(entryPath, files);
4895
- } else {
4896
- files.push(entryPath);
4897
- }
4898
- }
4899
- return files;
4900
- };
4901
-
4902
- const relative = (filePath) => path.relative(root, filePath).replaceAll('\\\\', '/');
4903
-
4904
- const isSourceFile = (filePath) => /\\.(?:ts|tsx|js|jsx)$/u.test(filePath);
4905
-
4906
- const isLocaleJson = (filePath) => {
4907
- const normalized = relative(filePath);
4908
- return /\\/locales\\/(en|cs)\\/[^/]+\\.json$/u.test(normalized);
4909
- };
4910
-
4911
- const readText = (filePath) => fs.readFileSync(filePath, 'utf-8');
4912
-
4913
- const branchIsUserCopy = (branch) => {
4914
- const value = branch.trim().replace(/,$/u, '');
4915
- if (allowedLanguageConditionalBranches.has(value)) {
4916
- return false;
4917
- }
4918
- return /^['"][^'"]{2,}['"]$/u.test(value);
4919
- };
4920
-
4921
- const checkRuntimeResources = (filePath, text) => {
4922
- if (!relative(filePath).endsWith('/src/modern.runtime.ts')) {
4923
- return;
4924
- }
4925
- const importsLocaleResources =
4926
- /import\\s+csResource\\s+from\\s+['"]\\.\\.\\/locales\\/cs\\/[^'"]+\\.json['"]/u.test(text) &&
4927
- /import\\s+enResource\\s+from\\s+['"]\\.\\.\\/locales\\/en\\/[^'"]+\\.json['"]/u.test(text);
4928
- if (!importsLocaleResources || !/initOptions\\s*:\\s*\\{[\\s\\S]*?\\bresources\\s*,/u.test(text)) {
4929
- fail(\`\${relative(filePath)} must register locale JSON resources in modern.runtime.ts so Worker SSR and hydration use the same first-render translations.\`);
4930
- }
4931
- };
4932
-
4933
- const checkLanguageConditionals = (filePath, text) => {
4934
- for (const match of text.matchAll(languageConditionalPattern)) {
4935
- const [, name, whenTrue = '', whenFalse = ''] = match;
4936
- if (branchIsUserCopy(whenTrue) || branchIsUserCopy(whenFalse)) {
4937
- fail(
4938
- \`\${relative(filePath)} contains manual \${name} copy branching. Put user-facing copy in i18n JSON resources.\`,
4939
- );
4940
- }
4941
- }
4942
- };
4943
-
4944
- const checkLiteralVisibleAttributes = (filePath, text) => {
4945
- if (!filePath.endsWith('.tsx') && !filePath.endsWith('.jsx')) {
4946
- return;
4947
- }
4948
- for (const attribute of visibleCopyAttributes) {
4949
- const pattern = new RegExp(\`\\\\b\${attribute}=["'][^"'{}]*[A-Za-z][^"'{}]*["']\`, 'u');
4950
- if (pattern.test(text)) {
4951
- fail(
4952
- \`\${relative(filePath)} contains literal \${attribute} copy. Use t(...) or route metadata for visible text.\`,
4953
- );
4954
- }
4955
- }
4956
- };
4957
-
4958
- const checkSplitPhraseKeys = (filePath, text) => {
4959
- if (/t\\(\\s*['"][^'"]+\\.(?:prefix|suffix|before|after)['"]\\s*\\)/u.test(text)) {
4960
- fail(
4961
- \`\${relative(filePath)} uses split phrase translation keys. Keep translator-owned phrases whole.\`,
4962
- );
4963
- }
4964
- };
4965
-
4966
- const checkBoundaryAttributes = (filePath, text) => {
4967
- if (!filePath.endsWith('.tsx') && !filePath.endsWith('.jsx')) {
4968
- return;
4969
- }
4970
- if (/\\bdata-mf-(?:boundary|remote|expose)=/u.test(text)) {
4971
- fail(
4972
- \`\${relative(filePath)} uses legacy data-mf-* boundary attributes. Use data-modern-boundary-id and data-modern-mf-expose.\`,
4973
- );
4974
- }
4975
- };
4976
-
4977
- const visitLocaleKeys = (value, visitor, pathParts = []) => {
4978
- if (!value || typeof value !== 'object' || Array.isArray(value)) {
4979
- return;
4980
- }
4981
- for (const [key, child] of Object.entries(value)) {
4982
- const nextPath = [...pathParts, key];
4983
- visitor(key, child, nextPath);
4984
- visitLocaleKeys(child, visitor, nextPath);
4985
- }
4986
- };
4987
-
4988
- const checkPluralResources = (filePath, json) => {
4989
- const language = relative(filePath).split('/locales/')[1]?.split('/')[0];
4990
- const requiredSuffixes =
4991
- language === 'cs' ? ['one', 'few', 'many', 'other'] : ['one', 'other'];
4992
- const groups = new Map();
4993
-
4994
- visitLocaleKeys(json, (key, value, pathParts) => {
4995
- if (typeof value === 'string' && value.includes('{{count}}')) {
4996
- const suffixMatch = key.match(/^(.*)_(one|few|many|other)$/u);
4997
- if (!suffixMatch) {
4998
- fail(
4999
- \`\${relative(filePath)} key \${pathParts.join('.')} contains {{count}} but is not plural-suffixed.\`,
5000
- );
5001
- }
5002
- const [, base = '', suffix = ''] = suffixMatch;
5003
- const parentPath = pathParts.slice(0, -1).join('.');
5004
- const groupKey = \`\${parentPath}.\${base}\`;
5005
- const existing = groups.get(groupKey) ?? new Set();
5006
- existing.add(suffix);
5007
- groups.set(groupKey, existing);
5008
- }
5009
- });
5010
-
5011
- for (const [group, suffixes] of groups) {
5012
- for (const suffix of requiredSuffixes) {
5013
- if (!suffixes.has(suffix)) {
5014
- fail(\`\${relative(filePath)} plural group \${group} is missing _\${suffix}.\`);
5015
- }
5016
- }
5017
- }
5018
- };
5019
-
5020
- const sourceFiles = sourceRoots.flatMap(sourceRoot =>
5021
- walk(path.join(root, sourceRoot)).filter(filePath => isSourceFile(filePath)),
5022
- );
5023
- for (const filePath of sourceFiles) {
5024
- const text = readText(filePath);
5025
- checkRuntimeResources(filePath, text);
5026
- checkLanguageConditionals(filePath, text);
5027
- checkLiteralVisibleAttributes(filePath, text);
5028
- checkSplitPhraseKeys(filePath, text);
5029
- checkBoundaryAttributes(filePath, text);
5030
- }
5031
-
5032
- const localeFiles = sourceRoots.flatMap(sourceRoot =>
5033
- walk(path.join(root, sourceRoot)).filter(filePath => isLocaleJson(filePath)),
5034
- );
5035
- for (const filePath of localeFiles) {
5036
- checkPluralResources(filePath, JSON.parse(readText(filePath)));
5037
- }
5038
-
5039
- console.log('UltraModern i18n and boundary guardrails validated');
4249
+ process.exitCode = runWorkspaceSourceCheck({
4250
+ cwd: root,
4251
+ sourceRoots: ['apps', 'verticals'],
4252
+ });
5040
4253
  `;
5041
4254
  }
5042
4255
  function createWorkspaceValidationScript(scope, enableTailwind, remotes = []) {
@@ -5268,6 +4481,16 @@ assert(rootPackage.modernjs?.packageSource?.config === './.modernjs/ultramodern-
5268
4481
  assert(rootPackage.modernjs?.packageSource?.strategy === packageSource.strategy, 'Root package source strategy must match metadata');
5269
4482
  assert(packageSource.strategy === 'workspace' || packageSource.strategy === 'install', 'Package source strategy must be workspace or install');
5270
4483
  assert(packageSource.strategy === 'install' || packageSource.modernPackages?.specifier === 'workspace:*', 'Workspace package source must be explicitly backed by workspace:*');
4484
+ const expectedModernDependency = packageName => {
4485
+ const alias = packageSource.modernPackages?.aliases?.[packageName];
4486
+ const specifier = packageSource.modernPackages?.specifier;
4487
+ return typeof alias === 'string' ? \`npm:\${alias}@\${specifier}\` : specifier;
4488
+ };
4489
+ assert(
4490
+ rootPackage.devDependencies?.['@modern-js/create'] ===
4491
+ expectedModernDependency('@modern-js/create'),
4492
+ 'Root must depend on @modern-js/create through package source metadata',
4493
+ );
5271
4494
  if (packageSource.strategy === 'install') {
5272
4495
  const installSpecifier = packageSource.modernPackages?.specifier;
5273
4496
  assert(
@@ -5284,6 +4507,7 @@ if (packageSource.strategy === 'install') {
5284
4507
  '@modern-js/plugin-i18n',
5285
4508
  '@modern-js/plugin-tanstack',
5286
4509
  '@modern-js/runtime',
4510
+ '@modern-js/create',
5287
4511
  ]) {
5288
4512
  assert(
5289
4513
  /^@[^/]+\\/.+/.test(modernAliases[modernPackageName] ?? ''),
@@ -5300,6 +4524,12 @@ assert(
5300
4524
  assert(rootPackage.scripts?.['cloudflare:build'] === expectedCloudflareBuildScript, 'Root cloudflare:build script is incorrect');
5301
4525
  assert(rootPackage.scripts?.['ultramodern:check'] === 'node ./scripts/validate-ultramodern-workspace.mjs', 'Root must expose ultramodern:check');
5302
4526
  assert(rootPackage.scripts?.['ultramodern:i18n-boundaries'] === 'node ./scripts/check-ultramodern-i18n-boundaries.mjs', 'Root must expose ultramodern:i18n-boundaries');
4527
+ const i18nBoundaryScript = readText('scripts/check-ultramodern-i18n-boundaries.mjs');
4528
+ assert(
4529
+ i18nBoundaryScript.includes("from '@modern-js/create/ultramodern-checks'") &&
4530
+ i18nBoundaryScript.includes('runWorkspaceSourceCheck'),
4531
+ 'Root i18n boundary script must call @modern-js/create/ultramodern-checks',
4532
+ );
5303
4533
  assert(rootPackage.scripts?.['ultramodern:assert-mf-types'] === 'node ./scripts/assert-mf-types.mjs', 'Root must expose ultramodern:assert-mf-types');
5304
4534
  assert(rootPackage.scripts?.['cloudflare:deploy'] === expectedCloudflareDeployScript, 'Root must expose cloudflare:deploy');
5305
4535
  assert(rootPackage.scripts?.['cloudflare:proof'] === 'node ./scripts/proof-cloudflare-version.mjs --out .codex/reports/cloudflare-version-proof/public-url-proof.json', 'Root must expose cloudflare:proof');
@@ -6316,1002 +5546,10 @@ function generateUltramodernWorkspace(options) {
6316
5546
  writeSharedPackages(options.targetDir, scope, packageSource);
6317
5547
  writeGeneratedWorkspaceScripts(options.targetDir, scope, enableTailwind, initialVerticals);
6318
5548
  }
6319
- const src_dirname = node_path.dirname(fileURLToPath(import.meta.url));
6320
- const templateDir = node_path.resolve(src_dirname, '..', 'template');
6321
- const semverPattern = /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
6322
- const semverTagPattern = /^v?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-[0-9A-Za-z.-]+)?$/;
6323
- const sha1Pattern = /^[0-9a-f]{40}$/;
6324
- const sha256Pattern = /^[0-9a-f]{64}$/;
6325
- const templateIdPattern = /^[a-z0-9][a-z0-9._-]*$/;
6326
- const packageNamePattern = /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+$/;
6327
- const src_TANSTACK_ROUTER_VERSION = '1.170.15';
6328
- const src_TAILWIND_VERSION = '4.3.0';
6329
- const src_TAILWIND_POSTCSS_VERSION = '4.3.0';
6330
- const src_PNPM_VERSION = '11.5.0';
6331
- const src_I18NEXT_VERSION = '26.3.1';
6332
- const src_REACT_VERSION = '^19.2.7';
6333
- const src_REACT_DOM_VERSION = '^19.2.7';
6334
- const REACT_I18NEXT_VERSION = '17.0.8';
6335
- const src_EFFECT_TSGO_VERSION = '0.14.0';
6336
- const src_TYPESCRIPT_NATIVE_PREVIEW_VERSION = '7.0.0-dev.20260606.1';
6337
- const HAPPY_DOM_VERSION = '^20.10.1';
6338
- const RSTEST_CORE_VERSION = '0.10.3';
6339
- const src_OXFMT_VERSION = '0.53.0';
6340
- const src_OXLINT_VERSION = '1.68.0';
6341
- const src_POSTCSS_VERSION = '^8.5.15';
6342
- const src_ULTRACITE_VERSION = '7.8.1';
6343
- const TYPES_REACT_VERSION = '^19.2.17';
6344
- const TYPES_REACT_DOM_VERSION = '^19.2.3';
6345
- const requiredDeniedPaths = [
6346
- '.git/**',
6347
- '.npmrc',
6348
- '.yarnrc',
6349
- '.env',
6350
- '.env.*',
6351
- 'node_modules/**',
6352
- 'dist/**'
6353
- ];
6354
- const requiredLifecycleDeniedScripts = [
6355
- 'preinstall',
6356
- 'install',
6357
- 'prepare'
6358
- ];
6359
- const requiredLifecycleAllowedScripts = [
6360
- 'postinstall'
6361
- ];
6362
- function getOptionValue(args, names) {
6363
- for (const name of names){
6364
- const prefix = `${name}=`;
6365
- const byEquals = args.find((arg)=>arg.startsWith(prefix));
6366
- if (byEquals) return byEquals.slice(prefix.length);
6367
- const index = args.findIndex((arg)=>arg === name);
6368
- if (-1 !== index && args[index + 1] && !args[index + 1].startsWith('-')) return args[index + 1];
6369
- }
6370
- }
6371
- const detectLanguage = ()=>{
6372
- const lang = getOptionValue(process.argv.slice(2), [
6373
- '--lang',
6374
- '-l'
6375
- ]);
6376
- if (lang) return 'zh' === lang ? 'zh' : 'en';
6377
- const detectedLang = getLocaleLanguage();
6378
- if ('zh' === detectedLang) return 'zh';
6379
- return 'en';
5549
+ const ultramodernWorkspaceVersions = {
5550
+ tanstackRouter: TANSTACK_ROUTER_VERSION,
5551
+ moduleFederation: MODULE_FEDERATION_VERSION,
5552
+ tailwind: TAILWIND_VERSION,
5553
+ tailwindPostcss: TAILWIND_POSTCSS_VERSION
6380
5554
  };
6381
- i18n.changeLanguage({
6382
- locale: detectLanguage()
6383
- });
6384
- function detectRouterFramework() {
6385
- const args = process.argv.slice(2);
6386
- if (args.includes('--tanstack')) return 'tanstack';
6387
- const routerValue = getOptionValue(args, [
6388
- '--router',
6389
- '-r'
6390
- ]);
6391
- if (!routerValue || 'tanstack' === routerValue) return 'tanstack';
6392
- if ('react-router' === routerValue) return 'react-router';
6393
- console.error(i18n.t(localeKeys.error.invalidRouter, {
6394
- router: routerValue
6395
- }));
6396
- process.exit(1);
6397
- }
6398
- function detectBffRuntime() {
6399
- const args = process.argv.slice(2);
6400
- const runtimeValue = getOptionValue(args, [
6401
- '--bff-runtime'
6402
- ]);
6403
- if (!runtimeValue) return 'effect';
6404
- if ('hono' === runtimeValue || 'effect' === runtimeValue) return runtimeValue;
6405
- console.error(i18n.t(localeKeys.error.invalidBffRuntime, {
6406
- runtime: runtimeValue
6407
- }));
6408
- process.exit(1);
6409
- }
6410
- function src_renderTemplate(template, data) {
6411
- const tagRegex = /\{\{(~?)(#if|#unless|\/if|\/unless)(?:\s+(\w+))?(~?)\}\}/g;
6412
- function renderConditionals(startIndex, expectedClose) {
6413
- let rendered = '';
6414
- let cursor = startIndex;
6415
- tagRegex.lastIndex = startIndex;
6416
- while(true){
6417
- const match = tagRegex.exec(template);
6418
- if (!match) return {
6419
- rendered: rendered + template.slice(cursor),
6420
- nextIndex: template.length
6421
- };
6422
- const [raw, , tag, condition, rightTrim] = match;
6423
- const tagIndex = match.index;
6424
- rendered += template.slice(cursor, tagIndex);
6425
- cursor = tagIndex + raw.length;
6426
- if ('#if' === tag || '#unless' === tag) {
6427
- const kind = '#if' === tag ? 'if' : 'unless';
6428
- const innerResult = renderConditionals(cursor, kind);
6429
- cursor = innerResult.nextIndex;
6430
- tagRegex.lastIndex = cursor;
6431
- const conditionValue = Boolean(data[condition ?? '']);
6432
- const shouldInclude = 'if' === kind ? conditionValue : !conditionValue;
6433
- if (shouldInclude) rendered += innerResult.rendered;
6434
- continue;
6435
- }
6436
- if ('/if' === tag || '/unless' === tag) {
6437
- const kind = '/if' === tag ? 'if' : 'unless';
6438
- if (expectedClose === kind) {
6439
- let nextIndex = cursor;
6440
- if ('~' === rightTrim) {
6441
- const trailingWhitespace = /^\s*/u.exec(template.slice(nextIndex));
6442
- nextIndex += trailingWhitespace?.[0].length ?? 0;
6443
- }
6444
- return {
6445
- rendered,
6446
- nextIndex
6447
- };
6448
- }
6449
- rendered += raw;
6450
- }
6451
- }
6452
- }
6453
- let result = renderConditionals(0).rendered;
6454
- const varRegex = /\{\{(\w+)\}\}/g;
6455
- result = result.replace(varRegex, (match, key)=>{
6456
- const value = data[key];
6457
- return null != value ? String(value) : match;
6458
- });
6459
- return result;
6460
- }
6461
- function normalizePathForManifest(filePath) {
6462
- return filePath.split(node_path.sep).join('/');
6463
- }
6464
- function isUnsafeRelativePath(filePath) {
6465
- return 0 === filePath.length || node_path.isAbsolute(filePath) || filePath.startsWith('/') || /^[A-Za-z]:[\\/]/.test(filePath) || filePath.split(/[\\/]+/).includes('..');
6466
- }
6467
- function src_hashFile(filePath) {
6468
- return node_crypto.createHash('sha256').update(node_fs.readFileSync(filePath)).digest('hex');
6469
- }
6470
- function getTemplateFiles(dir) {
6471
- const files = [];
6472
- function collect(currentDir) {
6473
- const entries = node_fs.readdirSync(currentDir, {
6474
- withFileTypes: true
6475
- }).sort((a, b)=>a.name.localeCompare(b.name));
6476
- for (const entry of entries){
6477
- const entryPath = node_path.join(currentDir, entry.name);
6478
- if (entry.isDirectory()) collect(entryPath);
6479
- else if (entry.isFile()) files.push(normalizePathForManifest(node_path.relative(dir, entryPath)));
6480
- }
6481
- }
6482
- collect(dir);
6483
- return files;
6484
- }
6485
- function src_hashTemplateTree(dir) {
6486
- const hash = node_crypto.createHash('sha256');
6487
- for (const relativePath of getTemplateFiles(dir)){
6488
- const fileHash = src_hashFile(node_path.join(dir, relativePath));
6489
- hash.update(relativePath);
6490
- hash.update('\0');
6491
- hash.update(fileHash);
6492
- hash.update('\0');
6493
- }
6494
- return hash.digest('hex');
6495
- }
6496
- function createBuiltinTemplateManifest(version) {
6497
- return {
6498
- schemaVersion: 1,
6499
- template: {
6500
- id: 'modernjs-ultramodern-app',
6501
- version,
6502
- displayName: 'Modern.js Ultramodern App',
6503
- description: 'Repository-owned Modern.js application scaffold with UltraModern preset defaults.',
6504
- compatibilityLane: 'ultramodern-mv',
6505
- minimumModernVersion: version
6506
- },
6507
- source: {
6508
- type: 'builtin',
6509
- name: 'modernjs-ultramodern-app',
6510
- repositoryPath: 'packages/toolkit/create/template'
6511
- },
6512
- integrity: {
6513
- checksums: [
6514
- {
6515
- algorithm: 'sha256',
6516
- value: src_hashTemplateTree(templateDir),
6517
- scope: 'source-tree'
6518
- }
6519
- ],
6520
- provenance: {
6521
- kind: 'repo-local',
6522
- issuer: '@modern-js/create',
6523
- subject: 'packages/toolkit/create/template'
6524
- }
6525
- },
6526
- materialization: {
6527
- targetRoot: 'generated-project-root',
6528
- allowedPaths: [
6529
- '.agents/**',
6530
- '.browserslistrc',
6531
- '.codex/**',
6532
- '.github/**',
6533
- '.gitignore',
6534
- '.mise.toml',
6535
- '.modernjs/**',
6536
- '.nvmrc',
6537
- 'AGENTS.md',
6538
- 'README.md',
6539
- 'api/**',
6540
- 'config/**',
6541
- 'lefthook.yml',
6542
- 'modern.config.ts',
6543
- 'oxfmt.config.ts',
6544
- 'oxlint.config.ts',
6545
- 'package.json',
6546
- 'pnpm-workspace.yaml',
6547
- 'postcss.config.mjs',
6548
- 'rstest.config.mts',
6549
- "scripts/**",
6550
- 'shared/**',
6551
- 'src/**',
6552
- 'tailwind.config.ts',
6553
- 'tests/**',
6554
- 'tsconfig.json'
6555
- ],
6556
- deniedPaths: requiredDeniedPaths,
6557
- overwritePolicy: 'deny-existing'
6558
- },
6559
- lifecyclePolicy: {
6560
- denyByDefault: true,
6561
- deniedScripts: requiredLifecycleDeniedScripts,
6562
- allowedScripts: requiredLifecycleAllowedScripts,
6563
- requiresExplicitOptIn: true
6564
- },
6565
- validation: {
6566
- schemaValidation: true,
6567
- sourceValidation: [
6568
- 'source-type-supported',
6569
- 'checksum-verified',
6570
- 'provenance-present'
6571
- ],
6572
- materializationValidation: [
6573
- 'path-boundary-allowlist',
6574
- 'path-boundary-denylist',
6575
- 'no-path-traversal',
6576
- 'no-absolute-paths',
6577
- 'overwrite-policy-enforced'
6578
- ],
6579
- postMaterializationValidation: [
6580
- 'ultramodern-contract-check',
6581
- 'agent-skill-postinstall-allowed',
6582
- 'github-workflow-security-enforced',
6583
- 'package-source-retained',
6584
- 'pnpm-11-policy-enforced',
6585
- 'rstest-smoke-tests',
6586
- 'template-manifest-retained'
6587
- ],
6588
- expectedCommands: [
6589
- 'mise install',
6590
- 'pnpm install',
6591
- 'pnpm test',
6592
- 'pnpm run ultramodern:check'
6593
- ]
6594
- }
6595
- };
6596
- }
6597
- function assertTemplateManifest(condition, message) {
6598
- if (!condition) throw new Error(`Template manifest validation failed: ${message}`);
6599
- }
6600
- function assertSafeManifestPath(filePath, label) {
6601
- assertTemplateManifest(!isUnsafeRelativePath(filePath), `${label} is unsafe`);
6602
- }
6603
- function validateTemplateSource(source) {
6604
- const sourceType = source.type;
6605
- assertTemplateManifest('builtin' === sourceType || 'npm' === sourceType || 'git' === sourceType || 'local' === sourceType, `unsupported source type "${source.type}"`);
6606
- if ('builtin' === source.type) {
6607
- assertTemplateManifest(templateIdPattern.test(source.name), 'builtin source name must be a template id');
6608
- if (source.repositoryPath) assertSafeManifestPath(source.repositoryPath, 'builtin repositoryPath');
6609
- }
6610
- if ('npm' === source.type) {
6611
- assertTemplateManifest(packageNamePattern.test(source.packageName), 'npm packageName must be exact package metadata');
6612
- assertTemplateManifest(semverPattern.test(source.version), 'npm source version must be an exact semver');
6613
- assertTemplateManifest(sha256Pattern.test(source.tarballSha256), 'npm source tarballSha256 must be sha256 hex');
6614
- }
6615
- if ('git' === source.type) {
6616
- assertTemplateManifest(sha1Pattern.test(source.checkoutSha), 'git checkoutSha must pin a commit');
6617
- if ('sha' === source.ref.kind) assertTemplateManifest(sha1Pattern.test(source.ref.sha), 'git sha ref must be pinned to a commit');
6618
- else {
6619
- assertTemplateManifest(semverTagPattern.test(source.ref.tag), 'git tag ref must be a semver tag');
6620
- assertTemplateManifest(sha1Pattern.test(source.ref.tagSha), 'git tag ref must include the resolved tag sha');
6621
- }
6622
- if (source.subdirectory) assertSafeManifestPath(source.subdirectory, 'git subdirectory');
6623
- }
6624
- if ('local' === source.type) {
6625
- assertSafeManifestPath(source.path, 'local source path');
6626
- assertTemplateManifest(true !== source.allowOutsideWorkspace, 'local source cannot allow outside workspace materialization');
6627
- }
6628
- }
6629
- function validateTemplateManifest(manifest) {
6630
- assertTemplateManifest(1 === manifest.schemaVersion, 'schemaVersion must be 1');
6631
- assertTemplateManifest(templateIdPattern.test(manifest.template.id), 'template.id must be a template id');
6632
- assertTemplateManifest(semverPattern.test(manifest.template.version), 'template.version must be exact semver');
6633
- assertTemplateManifest('ultramodern-mv' === manifest.template.compatibilityLane || 'ultramodern-shell' === manifest.template.compatibilityLane || 'ultramodern-remote' === manifest.template.compatibilityLane, 'template.compatibilityLane is unsupported');
6634
- if (manifest.template.minimumModernVersion) assertTemplateManifest(semverPattern.test(manifest.template.minimumModernVersion), 'template.minimumModernVersion must be exact semver');
6635
- validateTemplateSource(manifest.source);
6636
- assertTemplateManifest(manifest.integrity.checksums.length > 0, 'integrity.checksums must not be empty');
6637
- for (const checksum of manifest.integrity.checksums){
6638
- assertTemplateManifest('sha256' === checksum.algorithm, 'checksum algorithm must be sha256');
6639
- assertTemplateManifest(sha256Pattern.test(checksum.value), 'checksum value must be sha256 hex');
6640
- assertTemplateManifest('manifest' === checksum.scope || 'source-archive' === checksum.scope || 'source-tree' === checksum.scope || 'lockfile' === checksum.scope, 'checksum scope is unsupported');
6641
- }
6642
- assertTemplateManifest(manifest.integrity.provenance.kind && manifest.integrity.provenance.issuer && manifest.integrity.provenance.subject, 'provenance kind, issuer, and subject are required');
6643
- if (manifest.integrity.lockfile) {
6644
- assertSafeManifestPath(manifest.integrity.lockfile.path, 'lockfile path');
6645
- assertTemplateManifest(sha256Pattern.test(manifest.integrity.lockfile.sha256), 'lockfile sha256 must be sha256 hex');
6646
- }
6647
- assertTemplateManifest('generated-project-root' === manifest.materialization.targetRoot || 'workspace-package-root' === manifest.materialization.targetRoot, 'materialization.targetRoot is unsupported');
6648
- assertTemplateManifest(manifest.materialization.allowedPaths.length > 0, 'materialization.allowedPaths must not be empty');
6649
- for (const allowedPath of manifest.materialization.allowedPaths)assertSafeManifestPath(allowedPath.replace(/\/\*\*$/, '/placeholder'), 'allowed path');
6650
- for (const deniedPath of manifest.materialization.deniedPaths)assertSafeManifestPath(deniedPath.replace(/\/\*\*$/, '/placeholder'), 'denied path');
6651
- for (const deniedPath of requiredDeniedPaths)assertTemplateManifest(manifest.materialization.deniedPaths.includes(deniedPath), `materialization.deniedPaths must include ${deniedPath}`);
6652
- assertTemplateManifest(!manifest.materialization.overwritePolicy || 'deny-existing' === manifest.materialization.overwritePolicy || 'allow-generated-only' === manifest.materialization.overwritePolicy, 'materialization.overwritePolicy is unsupported');
6653
- assertTemplateManifest(true === manifest.lifecyclePolicy.denyByDefault, 'lifecyclePolicy.denyByDefault must be true');
6654
- for (const scriptName of requiredLifecycleDeniedScripts)assertTemplateManifest(manifest.lifecyclePolicy.deniedScripts.includes(scriptName), `lifecyclePolicy.deniedScripts must include ${scriptName}`);
6655
- assertTemplateManifest(JSON.stringify(manifest.lifecyclePolicy.allowedScripts) === JSON.stringify(requiredLifecycleAllowedScripts), 'lifecyclePolicy.allowedScripts must only allow generated postinstall');
6656
- assertTemplateManifest(true === manifest.validation.schemaValidation, 'validation.schemaValidation must be true');
6657
- for (const token of [
6658
- 'source-type-supported',
6659
- 'checksum-verified',
6660
- 'provenance-present'
6661
- ])assertTemplateManifest(manifest.validation.sourceValidation.includes(token), `validation.sourceValidation must include ${token}`);
6662
- for (const token of [
6663
- 'path-boundary-allowlist',
6664
- 'path-boundary-denylist',
6665
- 'no-path-traversal',
6666
- 'no-absolute-paths',
6667
- 'overwrite-policy-enforced'
6668
- ])assertTemplateManifest(manifest.validation.materializationValidation.includes(token), `validation.materializationValidation must include ${token}`);
6669
- assertTemplateManifest(manifest.validation.postMaterializationValidation.includes('template-manifest-retained'), 'validation.postMaterializationValidation must retain manifest evidence');
6670
- }
6671
- function matchesManifestPattern(pattern, relativePath) {
6672
- if (pattern.endsWith('/**')) {
6673
- const prefix = pattern.slice(0, -3);
6674
- return relativePath === prefix || relativePath.startsWith(`${prefix}/`);
6675
- }
6676
- if (pattern.endsWith('.*')) {
6677
- const prefix = pattern.slice(0, -1);
6678
- return relativePath.startsWith(prefix);
6679
- }
6680
- return relativePath === pattern;
6681
- }
6682
- function canMaterializePath(manifest, relativePath) {
6683
- if (isUnsafeRelativePath(relativePath)) throw new Error(`Unsafe template path rejected: ${relativePath}`);
6684
- if (manifest.materialization.deniedPaths.some((pattern)=>matchesManifestPattern(pattern, relativePath))) return false;
6685
- if (!manifest.materialization.allowedPaths.some((pattern)=>matchesManifestPattern(pattern, relativePath))) throw new Error(`Template path is not allowed by manifest: ${relativePath}`);
6686
- return true;
6687
- }
6688
- function writeTemplateManifestEvidence(targetDir, manifest) {
6689
- const evidencePath = node_path.join(targetDir, '.modernjs', 'mv-template-manifest.json');
6690
- const evidenceRelativePath = normalizePathForManifest(node_path.relative(targetDir, evidencePath));
6691
- if (!canMaterializePath(manifest, evidenceRelativePath)) throw new Error('Template manifest evidence path is denied by manifest');
6692
- node_fs.mkdirSync(node_path.dirname(evidencePath), {
6693
- recursive: true
6694
- });
6695
- node_fs.writeFileSync(evidencePath, `${JSON.stringify(manifest, null, 2)}\n`);
6696
- }
6697
- function readCreatePackageJson() {
6698
- const createPackageJson = node_path.resolve(src_dirname, '..', 'package.json');
6699
- return JSON.parse(node_fs.readFileSync(createPackageJson, 'utf-8'));
6700
- }
6701
- function isBleedingDevCreatePackage(createPackage) {
6702
- return createPackage.name === BLEEDINGDEV_CREATE_PACKAGE;
6703
- }
6704
- function getBleedingDevFrameworkVersion(createPackage, fallbackVersion) {
6705
- const frameworkVersion = createPackage.ultramodern?.frameworkVersion;
6706
- return 'string' == typeof frameworkVersion && frameworkVersion.length > 0 ? frameworkVersion : fallbackVersion;
6707
- }
6708
- function showVersion() {
6709
- const createPackage = readCreatePackageJson();
6710
- const version = createPackage.version || 'unknown';
6711
- console.log(i18n.t(localeKeys.version.message, {
6712
- version
6713
- }));
6714
- process.exit(0);
6715
- }
6716
- function showHelp() {
6717
- console.log(i18n.t(localeKeys.help.title));
6718
- console.log(i18n.t(localeKeys.help.description));
6719
- console.log('');
6720
- console.log(i18n.t(localeKeys.help.usage));
6721
- console.log(i18n.t(localeKeys.help.usageExample));
6722
- console.log('');
6723
- console.log(i18n.t(localeKeys.help.options));
6724
- console.log(i18n.t(localeKeys.help.optionHelp));
6725
- console.log(i18n.t(localeKeys.help.optionVersion));
6726
- console.log(i18n.t(localeKeys.help.optionLang));
6727
- console.log(i18n.t(localeKeys.help.optionRouter));
6728
- if (localeKeys.help.optionBff) console.log(i18n.t(localeKeys.help.optionBff));
6729
- if (localeKeys.help.optionBffRuntime) console.log(i18n.t(localeKeys.help.optionBffRuntime));
6730
- if (localeKeys.help.optionTailwind) console.log(i18n.t(localeKeys.help.optionTailwind));
6731
- if (localeKeys.help.optionWorkspace) console.log(i18n.t(localeKeys.help.optionWorkspace));
6732
- if (localeKeys.help.optionUltramodernWorkspace) console.log(i18n.t(localeKeys.help.optionUltramodernWorkspace));
6733
- if (localeKeys.help.optionUltramodernPackageSource) console.log(i18n.t(localeKeys.help.optionUltramodernPackageSource));
6734
- if (localeKeys.help.optionUltramodernPackageScope) console.log(i18n.t(localeKeys.help.optionUltramodernPackageScope));
6735
- if (localeKeys.help.optionUltramodernPackageNamePrefix) console.log(i18n.t(localeKeys.help.optionUltramodernPackageNamePrefix));
6736
- if (localeKeys.help.optionVertical) console.log(i18n.t(localeKeys.help.optionVertical));
6737
- console.log(i18n.t(localeKeys.help.optionSub));
6738
- console.log('');
6739
- console.log(i18n.t(localeKeys.help.examples));
6740
- console.log(i18n.t(localeKeys.help.example1));
6741
- console.log(i18n.t(localeKeys.help.example2));
6742
- console.log(i18n.t(localeKeys.help.example3));
6743
- if (localeKeys.help.example4) console.log(i18n.t(localeKeys.help.example4));
6744
- if (localeKeys.help.example5) console.log(i18n.t(localeKeys.help.example5));
6745
- if (localeKeys.help.example6) console.log(i18n.t(localeKeys.help.example6));
6746
- if (localeKeys.help.example7) console.log(i18n.t(localeKeys.help.example7));
6747
- if (localeKeys.help.example8) console.log(i18n.t(localeKeys.help.example8));
6748
- if (localeKeys.help.example9) console.log(i18n.t(localeKeys.help.example9));
6749
- if (localeKeys.help.example10) console.log(i18n.t(localeKeys.help.example10));
6750
- if (localeKeys.help.example11) console.log(i18n.t(localeKeys.help.example11));
6751
- if (localeKeys.help.example12) console.log(i18n.t(localeKeys.help.example12));
6752
- console.log('');
6753
- console.log(i18n.t(localeKeys.help.moreInfo));
6754
- console.log('');
6755
- process.exit(0);
6756
- }
6757
- function promptInput(question) {
6758
- const rl = node_readline.createInterface({
6759
- input: process.stdin,
6760
- output: process.stdout
6761
- });
6762
- return new Promise((resolve)=>{
6763
- rl.question(question, (answer)=>{
6764
- rl.close();
6765
- resolve(answer.trim());
6766
- });
6767
- });
6768
- }
6769
- function detectSubprojectFlag() {
6770
- const args = process.argv.slice(2);
6771
- if (args.includes('--sub') || args.includes('-s')) return true;
6772
- if (args.includes('--no-sub')) return false;
6773
- return null;
6774
- }
6775
- function detectTailwindFlag() {
6776
- const args = process.argv.slice(2);
6777
- return !args.includes('--no-tailwind');
6778
- }
6779
- function detectExplicitTailwindFlag() {
6780
- const args = process.argv.slice(2);
6781
- if (args.includes('--no-tailwind')) return false;
6782
- if (args.includes('--tailwind')) return true;
6783
- }
6784
- function detectWorkspaceProtocolFlag() {
6785
- const args = process.argv.slice(2);
6786
- return args.includes('--workspace');
6787
- }
6788
- function detectVerticalFlag() {
6789
- const args = process.argv.slice(2);
6790
- if (args.some((arg)=>arg.startsWith('--vertical='))) {
6791
- console.error('--vertical does not accept a value. Use: create <name> --vertical');
6792
- process.exit(1);
6793
- }
6794
- return args.includes('--vertical');
6795
- }
6796
- function detectUltramodernWorkspaceFlag() {
6797
- const args = process.argv.slice(2);
6798
- return args.includes(ULTRAMODERN_WORKSPACE_FLAG);
6799
- }
6800
- function detectUltramodernPackageSource(args, defaultPackageVersion, createPackage) {
6801
- const bleedingDevDefaults = isBleedingDevCreatePackage(createPackage);
6802
- const strategy = getOptionValue(args, [
6803
- '--ultramodern-package-source'
6804
- ]) ?? (bleedingDevDefaults ? 'install' : 'workspace');
6805
- if ('workspace' !== strategy && 'install' !== strategy) {
6806
- console.error('--ultramodern-package-source must be "workspace" or "install"');
6807
- process.exit(1);
6808
- }
6809
- const packageSourceStrategy = strategy;
6810
- const explicitRegistry = getOptionValue(args, [
6811
- '--ultramodern-package-registry'
6812
- ]);
6813
- const aliasScope = getOptionValue(args, [
6814
- '--ultramodern-package-scope'
6815
- ]) ?? (bleedingDevDefaults && 'install' === packageSourceStrategy && !explicitRegistry ? BLEEDINGDEV_PACKAGE_SCOPE : void 0);
6816
- return {
6817
- strategy: packageSourceStrategy,
6818
- modernPackageVersion: getOptionValue(args, [
6819
- '--ultramodern-package-version'
6820
- ]) ?? defaultPackageVersion,
6821
- registry: explicitRegistry,
6822
- aliasScope,
6823
- aliasPackageNamePrefix: getOptionValue(args, [
6824
- '--ultramodern-package-name-prefix'
6825
- ]) ?? (aliasScope ? BLEEDINGDEV_PACKAGE_NAME_PREFIX : void 0)
6826
- };
6827
- }
6828
- function hasExplicitUltramodernPackageSource(args, value) {
6829
- const configuredValue = getOptionValue(args, [
6830
- '--ultramodern-package-source'
6831
- ]);
6832
- return value ? configuredValue === value : void 0 !== configuredValue;
6833
- }
6834
- function readBleedingDevFrameworkVersionFromRegistry() {
6835
- const envVersion = process.env[BLEEDINGDEV_FRAMEWORK_VERSION_ENV]?.trim();
6836
- if (envVersion) {
6837
- if (!semverPattern.test(envVersion)) {
6838
- console.error(`${BLEEDINGDEV_FRAMEWORK_VERSION_ENV} must be a valid semver version`);
6839
- process.exit(1);
6840
- }
6841
- return envVersion;
6842
- }
6843
- try {
6844
- const rawVersion = runSetupCommand('npm', [
6845
- 'view',
6846
- `${BLEEDINGDEV_CREATE_PACKAGE}@latest`,
6847
- 'ultramodern.frameworkVersion',
6848
- '--json'
6849
- ]).trim();
6850
- const version = JSON.parse(rawVersion);
6851
- if ('string' == typeof version && semverPattern.test(version)) return version;
6852
- } catch {}
6853
- console.error([
6854
- `Could not resolve ${BLEEDINGDEV_CREATE_PACKAGE}@latest ultramodern.frameworkVersion.`,
6855
- 'Pass --workspace to use local workspace protocol dependencies,',
6856
- 'or pass --ultramodern-package-version with the exact BleedingDev framework cohort.'
6857
- ].join(' '));
6858
- process.exit(1);
6859
- }
6860
- function resolveInstallBackedPackageSource(args, createPackage, packageSource) {
6861
- const explicitVersion = getOptionValue(args, [
6862
- '--ultramodern-package-version'
6863
- ]);
6864
- const explicitRegistry = getOptionValue(args, [
6865
- '--ultramodern-package-registry'
6866
- ]);
6867
- const aliasScope = getOptionValue(args, [
6868
- '--ultramodern-package-scope'
6869
- ]) ?? packageSource.aliasScope ?? (explicitRegistry ? void 0 : BLEEDINGDEV_PACKAGE_SCOPE);
6870
- return {
6871
- ...packageSource,
6872
- strategy: 'install',
6873
- modernPackageVersion: explicitVersion ?? (isBleedingDevCreatePackage(createPackage) ? packageSource.modernPackageVersion : readBleedingDevFrameworkVersionFromRegistry()),
6874
- aliasScope,
6875
- aliasPackageNamePrefix: getOptionValue(args, [
6876
- '--ultramodern-package-name-prefix'
6877
- ]) ?? packageSource.aliasPackageNamePrefix ?? (aliasScope ? BLEEDINGDEV_PACKAGE_NAME_PREFIX : void 0)
6878
- };
6879
- }
6880
- function resolveSingleAppPackageSource(args, createPackage, packageSource, useWorkspaceProtocol) {
6881
- if (useWorkspaceProtocol) return {
6882
- ...packageSource,
6883
- strategy: 'workspace',
6884
- modernPackageVersion: WORKSPACE_PACKAGE_VERSION
6885
- };
6886
- return resolveInstallBackedPackageSource(args, createPackage, packageSource);
6887
- }
6888
- function resolveWorkspacePackageSource(args, createPackage, packageSource) {
6889
- if (hasExplicitUltramodernPackageSource(args, 'workspace')) return {
6890
- ...packageSource,
6891
- strategy: 'workspace',
6892
- modernPackageVersion: WORKSPACE_PACKAGE_VERSION
6893
- };
6894
- return resolveInstallBackedPackageSource(args, createPackage, packageSource);
6895
- }
6896
- function createSingleAppPackageSourceEvidence(packageSource) {
6897
- return {
6898
- schemaVersion: 1,
6899
- preset: 'presetUltramodern',
6900
- strategy: packageSource.strategy,
6901
- modernPackages: createModernPackagesMetadata(ULTRAMODERN_SINGLE_APP_MODERN_PACKAGES, packageSource, {
6902
- includeAliases: 'install' === packageSource.strategy
6903
- })
6904
- };
6905
- }
6906
- function writeSingleAppPackageSourceEvidence(targetDir, packageSource) {
6907
- const evidencePath = node_path.join(targetDir, '.modernjs', 'ultramodern-package-source.json');
6908
- node_fs.mkdirSync(node_path.dirname(evidencePath), {
6909
- recursive: true
6910
- });
6911
- node_fs.writeFileSync(evidencePath, `${JSON.stringify(createSingleAppPackageSourceEvidence(packageSource), null, 2)}\n`);
6912
- }
6913
- function runSetupCommand(command, args, options = {}) {
6914
- return execFileSync(command, args, {
6915
- cwd: options.cwd,
6916
- encoding: 'utf-8',
6917
- stdio: options.stdio ?? [
6918
- 'ignore',
6919
- 'pipe',
6920
- 'pipe'
6921
- ]
6922
- });
6923
- }
6924
- function commandExists(command) {
6925
- try {
6926
- runSetupCommand(command, [
6927
- '--version'
6928
- ], {
6929
- stdio: 'ignore'
6930
- });
6931
- return true;
6932
- } catch {
6933
- return false;
6934
- }
6935
- }
6936
- function installGitForGeneratedProject() {
6937
- if (commandExists('git')) return;
6938
- const runShell = (script)=>runSetupCommand('sh', [
6939
- '-lc',
6940
- script
6941
- ], {
6942
- stdio: 'inherit'
6943
- });
6944
- const sudo = 'function' == typeof process.getuid && 0 === process.getuid() ? '' : 'sudo ';
6945
- if (commandExists('brew')) runSetupCommand('brew', [
6946
- 'install',
6947
- 'git'
6948
- ], {
6949
- stdio: 'inherit'
6950
- });
6951
- else if ('linux' === process.platform && commandExists('apt-get')) runShell(`${sudo}apt-get update && ${sudo}apt-get install -y git`);
6952
- else if ('linux' === process.platform && commandExists('dnf')) runShell(`${sudo}dnf install -y git`);
6953
- else if ('linux' === process.platform && commandExists('yum')) runShell(`${sudo}yum install -y git`);
6954
- else if ('linux' === process.platform && commandExists('apk')) runShell('apk add --no-cache git');
6955
- if (!commandExists('git')) throw new Error('Git is required for UltraModern setup. Install git and rerun create, or run pnpm skills:install after installing git.');
6956
- }
6957
- function isInsideGitWorkTree(targetDir) {
6958
- try {
6959
- return 'true' === runSetupCommand('git', [
6960
- 'rev-parse',
6961
- '--is-inside-work-tree'
6962
- ], {
6963
- cwd: targetDir
6964
- }).trim();
6965
- } catch {
6966
- return false;
6967
- }
6968
- }
6969
- function initializeGeneratedGitRepository(targetDir) {
6970
- installGitForGeneratedProject();
6971
- if (isInsideGitWorkTree(targetDir)) return;
6972
- try {
6973
- runSetupCommand('git', [
6974
- 'init',
6975
- '-b',
6976
- 'main'
6977
- ], {
6978
- cwd: targetDir,
6979
- stdio: 'inherit'
6980
- });
6981
- } catch {
6982
- runSetupCommand('git', [
6983
- 'init'
6984
- ], {
6985
- cwd: targetDir,
6986
- stdio: 'inherit'
6987
- });
6988
- runSetupCommand('git', [
6989
- 'branch',
6990
- '-M',
6991
- 'main'
6992
- ], {
6993
- cwd: targetDir,
6994
- stdio: 'inherit'
6995
- });
6996
- }
6997
- }
6998
- function isDirectoryEmpty(dirPath) {
6999
- if (!node_fs.existsSync(dirPath)) return false;
7000
- try {
7001
- const files = node_fs.readdirSync(dirPath);
7002
- return 0 === files.length;
7003
- } catch {
7004
- return false;
7005
- }
7006
- }
7007
- async function getProjectName() {
7008
- const args = process.argv.slice(2);
7009
- const optionWithValue = new Set([
7010
- '--lang',
7011
- '-l',
7012
- '--router',
7013
- '-r',
7014
- '--bff-runtime',
7015
- '--ultramodern-package-source',
7016
- '--ultramodern-package-version',
7017
- '--ultramodern-package-registry',
7018
- '--ultramodern-package-scope',
7019
- '--ultramodern-package-name-prefix'
7020
- ]);
7021
- const optionWithoutValue = new Set([
7022
- '--help',
7023
- '-h',
7024
- '--version',
7025
- '-v',
7026
- '--sub',
7027
- '-s',
7028
- '--no-sub',
7029
- '--tanstack',
7030
- '--bff',
7031
- '--tailwind',
7032
- '--no-tailwind',
7033
- '--workspace',
7034
- '--vertical',
7035
- ULTRAMODERN_WORKSPACE_FLAG
7036
- ]);
7037
- const positionalArgs = [];
7038
- for(let i = 0; i < args.length; i++){
7039
- const arg = args[i];
7040
- if (!optionWithoutValue.has(arg)) {
7041
- if (optionWithValue.has(arg)) {
7042
- i += 1;
7043
- continue;
7044
- }
7045
- if (!(arg.startsWith('--lang=') || arg.startsWith('--router=') || arg.startsWith('--bff-runtime=') || arg.startsWith('--ultramodern-package-source=') || arg.startsWith('--ultramodern-package-version=') || arg.startsWith('--ultramodern-package-registry=') || arg.startsWith('--ultramodern-package-scope=') || arg.startsWith('--ultramodern-package-name-prefix='))) positionalArgs.push(arg);
7046
- }
7047
- }
7048
- if (positionalArgs.length > 1) {
7049
- console.error(`Unexpected positional argument: ${positionalArgs[1]}`);
7050
- process.exit(1);
7051
- }
7052
- const projectNameArg = positionalArgs[0];
7053
- if (projectNameArg) {
7054
- if ('.' === projectNameArg) return {
7055
- name: node_path.basename(process.cwd()),
7056
- useCurrentDir: true
7057
- };
7058
- return {
7059
- name: projectNameArg,
7060
- useCurrentDir: false
7061
- };
7062
- }
7063
- const currentDir = process.cwd();
7064
- if (isDirectoryEmpty(currentDir)) return {
7065
- name: node_path.basename(currentDir),
7066
- useCurrentDir: true
7067
- };
7068
- const projectName = await promptInput(i18n.t(localeKeys.prompt.projectName));
7069
- if (!projectName) {
7070
- console.error(i18n.t(localeKeys.error.projectNameEmpty));
7071
- process.exit(1);
7072
- }
7073
- return {
7074
- name: projectName,
7075
- useCurrentDir: false
7076
- };
7077
- }
7078
- async function main() {
7079
- const args = process.argv.slice(2);
7080
- if (args.includes('--help') || args.includes('-h')) return void showHelp();
7081
- if (args.includes('--version') || args.includes('-v')) return void showVersion();
7082
- console.log(`\n${i18n.t(localeKeys.message.welcome)}\n`);
7083
- const { name: projectName, useCurrentDir } = await getProjectName();
7084
- const targetDir = useCurrentDir ? process.cwd() : node_path.isAbsolute(projectName) ? projectName : node_path.resolve(process.cwd(), projectName);
7085
- const generatedPackageName = useCurrentDir || node_path.isAbsolute(projectName) ? node_path.basename(targetDir) : projectName;
7086
- const createPackage = readCreatePackageJson();
7087
- const version = createPackage.version || 'latest';
7088
- const ultramodernPackageVersion = isBleedingDevCreatePackage(createPackage) ? getBleedingDevFrameworkVersion(createPackage, version) : version;
7089
- const addVertical = detectVerticalFlag();
7090
- if (addVertical) {
7091
- const overridePackageSource = args.some((arg)=>arg.startsWith('--ultramodern-package-')) ? detectUltramodernPackageSource(args, ultramodernPackageVersion, createPackage) : void 0;
7092
- addUltramodernVertical({
7093
- workspaceRoot: process.cwd(),
7094
- name: generatedPackageName,
7095
- modernVersion: version,
7096
- enableTailwind: detectExplicitTailwindFlag(),
7097
- packageSource: overridePackageSource
7098
- });
7099
- const dim = '\x1b[2m\x1b[3m';
7100
- const reset = '\x1b[0m';
7101
- console.log(`${i18n.t(localeKeys.message.success)}\n`);
7102
- console.log(`${dim} pnpm ultramodern:check${reset}\n`);
7103
- return;
7104
- }
7105
- if (node_fs.existsSync(targetDir)) {
7106
- const files = node_fs.readdirSync(targetDir);
7107
- if (files.length > 0) {
7108
- console.error(i18n.t(localeKeys.error.directoryExists, {
7109
- projectName
7110
- }));
7111
- process.exit(1);
7112
- }
7113
- }
7114
- const generateWorkspace = detectUltramodernWorkspaceFlag();
7115
- if (generateWorkspace) {
7116
- const packageSource = resolveWorkspacePackageSource(args, createPackage, detectUltramodernPackageSource(args, ultramodernPackageVersion, createPackage));
7117
- generateUltramodernWorkspace({
7118
- targetDir,
7119
- packageName: generatedPackageName,
7120
- modernVersion: version,
7121
- enableTailwind: detectTailwindFlag(),
7122
- packageSource
7123
- });
7124
- initializeGeneratedGitRepository(targetDir);
7125
- const dim = '\x1b[2m\x1b[3m';
7126
- const reset = '\x1b[0m';
7127
- console.log(`${i18n.t(localeKeys.message.success)}\n`);
7128
- console.log(i18n.t(localeKeys.message.nextSteps));
7129
- if (!useCurrentDir) console.log(`${dim} ${i18n.t(localeKeys.message.step1, {
7130
- projectName
7131
- })}${reset}`);
7132
- console.log(`${dim} ${i18n.t(localeKeys.message.step2)}${reset}`);
7133
- console.log(`${dim} pnpm ultramodern:check${reset}`);
7134
- console.log(`${dim} ${i18n.t(localeKeys.message.step3)}${reset}\n`);
7135
- return;
7136
- }
7137
- const subprojectFlag = detectSubprojectFlag();
7138
- const isSubproject = true === subprojectFlag;
7139
- const routerFramework = detectRouterFramework();
7140
- const bffRuntime = detectBffRuntime();
7141
- const enableTailwind = detectTailwindFlag();
7142
- const useWorkspaceProtocol = detectWorkspaceProtocolFlag();
7143
- const packageSource = resolveSingleAppPackageSource(args, createPackage, detectUltramodernPackageSource(args, ultramodernPackageVersion, createPackage), useWorkspaceProtocol);
7144
- const templateManifest = createBuiltinTemplateManifest('install' === packageSource.strategy ? packageSource.modernPackageVersion : version);
7145
- validateTemplateManifest(templateManifest);
7146
- copyTemplate(templateDir, targetDir, {
7147
- packageName: generatedPackageName,
7148
- version: 'workspace' === packageSource.strategy ? WORKSPACE_PACKAGE_VERSION : packageSource.modernPackageVersion,
7149
- runtimeVersion: modernPackageSpecifier('@modern-js/runtime', packageSource),
7150
- appToolsVersion: modernPackageSpecifier('@modern-js/app-tools', packageSource),
7151
- adapterRstestVersion: modernPackageSpecifier('@modern-js/adapter-rstest', packageSource),
7152
- tsconfigVersion: modernPackageSpecifier('@modern-js/tsconfig', packageSource),
7153
- pluginTanstackVersion: modernPackageSpecifier('@modern-js/plugin-tanstack', packageSource),
7154
- pluginBffVersion: modernPackageSpecifier('@modern-js/plugin-bff', packageSource),
7155
- pluginI18nVersion: modernPackageSpecifier('@modern-js/plugin-i18n', packageSource),
7156
- tanstackRouterVersion: src_TANSTACK_ROUTER_VERSION,
7157
- i18nextVersion: src_I18NEXT_VERSION,
7158
- reactVersion: src_REACT_VERSION,
7159
- reactDomVersion: src_REACT_DOM_VERSION,
7160
- reactI18nextVersion: REACT_I18NEXT_VERSION,
7161
- effectTsgoVersion: src_EFFECT_TSGO_VERSION,
7162
- typescriptNativePreviewVersion: src_TYPESCRIPT_NATIVE_PREVIEW_VERSION,
7163
- rstestCoreVersion: RSTEST_CORE_VERSION,
7164
- happyDomVersion: HAPPY_DOM_VERSION,
7165
- oxfmtVersion: src_OXFMT_VERSION,
7166
- oxlintVersion: src_OXLINT_VERSION,
7167
- postcssVersion: src_POSTCSS_VERSION,
7168
- ultraciteVersion: src_ULTRACITE_VERSION,
7169
- typesReactVersion: TYPES_REACT_VERSION,
7170
- typesReactDomVersion: TYPES_REACT_DOM_VERSION,
7171
- tailwindVersion: src_TAILWIND_VERSION,
7172
- tailwindPostcssVersion: src_TAILWIND_POSTCSS_VERSION,
7173
- pnpmVersion: src_PNPM_VERSION,
7174
- isSubproject,
7175
- routerFramework,
7176
- bffRuntime,
7177
- enableTailwind,
7178
- templateManifest
7179
- });
7180
- const targetPackageJson = node_path.join(targetDir, 'package.json');
7181
- const packageJson = JSON.parse(node_fs.readFileSync(targetPackageJson, 'utf-8'));
7182
- packageJson.name = generatedPackageName;
7183
- packageJson.modernjs = {
7184
- ...packageJson.modernjs ?? {},
7185
- preset: 'presetUltramodern',
7186
- packageSource: {
7187
- strategy: packageSource.strategy,
7188
- config: './.modernjs/ultramodern-package-source.json'
7189
- }
7190
- };
7191
- if (isSubproject) {
7192
- delete packageJson['lint-staged'];
7193
- delete packageJson['simple-git-hooks'];
7194
- if (packageJson.scripts) {
7195
- delete packageJson.scripts.prepare;
7196
- delete packageJson.scripts.format;
7197
- delete packageJson.scripts['format:check'];
7198
- delete packageJson.scripts.lint;
7199
- delete packageJson.scripts['lint:fix'];
7200
- delete packageJson.scripts['skills:install'];
7201
- delete packageJson.scripts['skills:check'];
7202
- delete packageJson.scripts.postinstall;
7203
- }
7204
- if (packageJson.devDependencies) {
7205
- delete packageJson.devDependencies['lint-staged'];
7206
- delete packageJson.devDependencies.lefthook;
7207
- delete packageJson.devDependencies['simple-git-hooks'];
7208
- delete packageJson.devDependencies.oxlint;
7209
- delete packageJson.devDependencies.oxfmt;
7210
- delete packageJson.devDependencies.ultracite;
7211
- }
7212
- node_fs.rmSync(node_path.join(targetDir, '.codex'), {
7213
- recursive: true,
7214
- force: true
7215
- });
7216
- node_fs.rmSync(node_path.join(targetDir, 'lefthook.yml'), {
7217
- force: true
7218
- });
7219
- }
7220
- node_fs.writeFileSync(targetPackageJson, `${JSON.stringify(packageJson, null, 2)}\n`);
7221
- writeTemplateManifestEvidence(targetDir, templateManifest);
7222
- writeSingleAppPackageSourceEvidence(targetDir, packageSource);
7223
- if (!isSubproject) initializeGeneratedGitRepository(targetDir);
7224
- const dim = '\x1b[2m\x1b[3m';
7225
- const reset = '\x1b[0m';
7226
- console.log(`${i18n.t(localeKeys.message.success)}\n`);
7227
- console.log(i18n.t(localeKeys.message.nextSteps));
7228
- if (!useCurrentDir) console.log(`${dim} ${i18n.t(localeKeys.message.step1, {
7229
- projectName
7230
- })}${reset}`);
7231
- console.log(`${dim} ${i18n.t(localeKeys.message.step2)}${reset}`);
7232
- console.log(`${dim} ${i18n.t(localeKeys.message.step3)}${reset}\n`);
7233
- }
7234
- function copyTemplate(src, dest, options) {
7235
- node_fs.mkdirSync(dest, {
7236
- recursive: true
7237
- });
7238
- const excludeInSubproject = [
7239
- '.agents',
7240
- '.github',
7241
- '.gitignore.handlebars',
7242
- 'AGENTS.md',
7243
- '.npmrc',
7244
- '.nvmrc',
7245
- 'oxfmt.config.ts',
7246
- 'oxlint.config.ts'
7247
- ];
7248
- function copyRecursive(srcDir, destDir) {
7249
- const entries = node_fs.readdirSync(srcDir, {
7250
- withFileTypes: true
7251
- });
7252
- for (const entry of entries){
7253
- if (options.isSubproject && excludeInSubproject.includes(entry.name)) continue;
7254
- const srcPath = node_path.join(srcDir, entry.name);
7255
- let destPath = node_path.join(destDir, entry.name);
7256
- const sourceRelativePath = normalizePathForManifest(node_path.relative(src, srcPath));
7257
- const finalRelativePath = normalizePathForManifest(sourceRelativePath.replace(/\.handlebars$/, ''));
7258
- if (!(!canMaterializePath(options.templateManifest, finalRelativePath) || entry.isDirectory() && options.templateManifest.materialization.deniedPaths.some((pattern)=>matchesManifestPattern(pattern, finalRelativePath)))) if (entry.isDirectory()) {
7259
- node_fs.mkdirSync(destPath, {
7260
- recursive: true
7261
- });
7262
- copyRecursive(srcPath, destPath);
7263
- } else if (entry.name.endsWith('.handlebars')) {
7264
- const templateContent = node_fs.readFileSync(srcPath, 'utf-8');
7265
- const rendered = src_renderTemplate(templateContent, {
7266
- packageName: options.packageName,
7267
- version: options.version,
7268
- runtimeVersion: options.runtimeVersion,
7269
- appToolsVersion: options.appToolsVersion,
7270
- adapterRstestVersion: options.adapterRstestVersion,
7271
- tsconfigVersion: options.tsconfigVersion,
7272
- pluginTanstackVersion: options.pluginTanstackVersion,
7273
- pluginBffVersion: options.pluginBffVersion,
7274
- pluginI18nVersion: options.pluginI18nVersion,
7275
- tanstackRouterVersion: options.tanstackRouterVersion,
7276
- i18nextVersion: options.i18nextVersion,
7277
- reactVersion: options.reactVersion,
7278
- reactDomVersion: options.reactDomVersion,
7279
- reactI18nextVersion: options.reactI18nextVersion,
7280
- effectTsgoVersion: options.effectTsgoVersion,
7281
- typescriptNativePreviewVersion: options.typescriptNativePreviewVersion,
7282
- rstestCoreVersion: options.rstestCoreVersion,
7283
- happyDomVersion: options.happyDomVersion,
7284
- oxfmtVersion: options.oxfmtVersion,
7285
- oxlintVersion: options.oxlintVersion,
7286
- postcssVersion: options.postcssVersion,
7287
- ultraciteVersion: options.ultraciteVersion,
7288
- typesReactVersion: options.typesReactVersion,
7289
- typesReactDomVersion: options.typesReactDomVersion,
7290
- tailwindVersion: options.tailwindVersion,
7291
- tailwindPostcssVersion: options.tailwindPostcssVersion,
7292
- pnpmVersion: options.pnpmVersion,
7293
- isSubproject: options.isSubproject,
7294
- isTanstackRouter: 'tanstack' === options.routerFramework,
7295
- enableBff: 'none' !== options.bffRuntime,
7296
- useEffectBff: 'effect' === options.bffRuntime,
7297
- useHonoBff: 'hono' === options.bffRuntime,
7298
- bffRuntime: options.bffRuntime,
7299
- enableTailwind: options.enableTailwind,
7300
- routerRuntimeImport: 'tanstack' === options.routerFramework ? '@modern-js/plugin-tanstack/runtime' : '@modern-js/runtime/router'
7301
- });
7302
- if (0 === rendered.trim().length) continue;
7303
- destPath = destPath.replace(/\.handlebars$/, '');
7304
- if ('deny-existing' === options.templateManifest.materialization.overwritePolicy && node_fs.existsSync(destPath)) throw new Error(`Template refused to overwrite existing file: ${finalRelativePath}`);
7305
- node_fs.writeFileSync(destPath, rendered, 'utf-8');
7306
- } else {
7307
- if ('deny-existing' === options.templateManifest.materialization.overwritePolicy && node_fs.existsSync(destPath)) throw new Error(`Template refused to overwrite existing file: ${finalRelativePath}`);
7308
- node_fs.copyFileSync(srcPath, destPath);
7309
- }
7310
- }
7311
- }
7312
- copyRecursive(src, dest);
7313
- }
7314
- main().catch((error)=>{
7315
- console.error(i18n.t(localeKeys.error.createFailed), error);
7316
- process.exit(1);
7317
- });
5555
+ export { ULTRAMODERN_WORKSPACE_FLAG, addUltramodernVertical, generateUltramodernWorkspace, ultramodernWorkspaceVersions };