@rs-x/cli 2.0.0-next.2 → 2.0.0-next.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/bin/rsx.cjs +1373 -334
- package/package.json +5 -1
- package/{rs-x-vscode-extension-2.0.0-next.2.vsix → rs-x-vscode-extension-2.0.0-next.20.vsix} +0 -0
- package/scripts/prepare-local-rsx-packages.sh +20 -0
- package/scripts/verify-rsx-cli-mutations.sh +258 -0
- package/scripts/verify-rsx-projects.sh +134 -0
- package/scripts/verify-rsx-setup.sh +186 -0
- package/templates/angular-demo/README.md +115 -0
- package/templates/angular-demo/src/app/app.component.css +97 -0
- package/templates/angular-demo/src/app/app.component.html +58 -0
- package/templates/angular-demo/src/app/app.component.ts +52 -0
- package/templates/angular-demo/src/app/virtual-table/row-data.ts +35 -0
- package/templates/angular-demo/src/app/virtual-table/row-model.ts +45 -0
- package/templates/angular-demo/src/app/virtual-table/virtual-table-data.service.ts +136 -0
- package/templates/angular-demo/src/app/virtual-table/virtual-table-model.ts +224 -0
- package/templates/angular-demo/src/app/virtual-table/virtual-table.component.css +174 -0
- package/templates/angular-demo/src/app/virtual-table/virtual-table.component.html +50 -0
- package/templates/angular-demo/src/app/virtual-table/virtual-table.component.ts +83 -0
- package/templates/angular-demo/src/index.html +11 -0
- package/templates/angular-demo/src/main.ts +16 -0
- package/templates/angular-demo/src/styles.css +261 -0
- package/templates/next-demo/README.md +26 -0
- package/templates/next-demo/app/globals.css +431 -0
- package/templates/next-demo/app/layout.tsx +22 -0
- package/templates/next-demo/app/page.tsx +5 -0
- package/templates/next-demo/components/demo-app.tsx +114 -0
- package/templates/next-demo/components/virtual-table-row.tsx +40 -0
- package/templates/next-demo/components/virtual-table-shell.tsx +86 -0
- package/templates/next-demo/hooks/use-virtual-table-controller.ts +26 -0
- package/templates/next-demo/hooks/use-virtual-table-viewport.ts +41 -0
- package/templates/next-demo/lib/row-data.ts +35 -0
- package/templates/next-demo/lib/row-model.ts +45 -0
- package/templates/next-demo/lib/rsx-bootstrap.ts +46 -0
- package/templates/next-demo/lib/virtual-table-controller.ts +259 -0
- package/templates/next-demo/lib/virtual-table-data.service.ts +132 -0
- package/templates/react-demo/README.md +113 -0
- package/templates/react-demo/index.html +12 -0
- package/templates/react-demo/src/app/app.tsx +87 -0
- package/templates/react-demo/src/app/hooks/use-virtual-table-controller.ts +24 -0
- package/templates/react-demo/src/app/hooks/use-virtual-table-viewport.ts +39 -0
- package/templates/react-demo/src/app/virtual-table/row-data.ts +35 -0
- package/templates/react-demo/src/app/virtual-table/row-model.ts +45 -0
- package/templates/react-demo/src/app/virtual-table/virtual-table-controller.ts +259 -0
- package/templates/react-demo/src/app/virtual-table/virtual-table-data.service.ts +132 -0
- package/templates/react-demo/src/app/virtual-table/virtual-table-row.tsx +38 -0
- package/templates/react-demo/src/app/virtual-table/virtual-table-shell.tsx +84 -0
- package/templates/react-demo/src/main.tsx +24 -0
- package/templates/react-demo/src/rsx-bootstrap.ts +48 -0
- package/templates/react-demo/src/styles.css +422 -0
- package/templates/react-demo/tsconfig.json +17 -0
- package/templates/react-demo/vite.config.ts +6 -0
- package/templates/vue-demo/README.md +27 -0
- package/templates/vue-demo/src/App.vue +89 -0
- package/templates/vue-demo/src/components/VirtualTableRow.vue +33 -0
- package/templates/vue-demo/src/components/VirtualTableShell.vue +71 -0
- package/templates/vue-demo/src/composables/use-virtual-table-controller.ts +33 -0
- package/templates/vue-demo/src/composables/use-virtual-table-viewport.ts +40 -0
- package/templates/vue-demo/src/env.d.ts +10 -0
- package/templates/vue-demo/src/lib/row-data.ts +35 -0
- package/templates/vue-demo/src/lib/row-model.ts +45 -0
- package/templates/vue-demo/src/lib/rsx-bootstrap.ts +46 -0
- package/templates/vue-demo/src/lib/virtual-table-controller.ts +259 -0
- package/templates/vue-demo/src/lib/virtual-table-data.service.ts +132 -0
- package/templates/vue-demo/src/main.ts +13 -0
- package/templates/vue-demo/src/style.css +440 -0
package/bin/rsx.cjs
CHANGED
|
@@ -5,8 +5,40 @@ const path = require('node:path');
|
|
|
5
5
|
const readline = require('node:readline/promises');
|
|
6
6
|
const { spawnSync } = require('node:child_process');
|
|
7
7
|
|
|
8
|
-
const CLI_VERSION =
|
|
8
|
+
const CLI_VERSION = (() => {
|
|
9
|
+
try {
|
|
10
|
+
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
11
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
12
|
+
return packageJson.version ?? '0.0.0';
|
|
13
|
+
} catch {
|
|
14
|
+
return '0.0.0';
|
|
15
|
+
}
|
|
16
|
+
})();
|
|
9
17
|
const VS_CODE_EXTENSION_ID = 'rs-x.rs-x-vscode-extension';
|
|
18
|
+
const ANGULAR_DEMO_TEMPLATE_DIR = path.join(
|
|
19
|
+
__dirname,
|
|
20
|
+
'..',
|
|
21
|
+
'templates',
|
|
22
|
+
'angular-demo',
|
|
23
|
+
);
|
|
24
|
+
const REACT_DEMO_TEMPLATE_DIR = path.join(
|
|
25
|
+
__dirname,
|
|
26
|
+
'..',
|
|
27
|
+
'templates',
|
|
28
|
+
'react-demo',
|
|
29
|
+
);
|
|
30
|
+
const VUE_DEMO_TEMPLATE_DIR = path.join(
|
|
31
|
+
__dirname,
|
|
32
|
+
'..',
|
|
33
|
+
'templates',
|
|
34
|
+
'vue-demo',
|
|
35
|
+
);
|
|
36
|
+
const NEXT_DEMO_TEMPLATE_DIR = path.join(
|
|
37
|
+
__dirname,
|
|
38
|
+
'..',
|
|
39
|
+
'templates',
|
|
40
|
+
'next-demo',
|
|
41
|
+
);
|
|
10
42
|
const RUNTIME_PACKAGES = [
|
|
11
43
|
'@rs-x/core',
|
|
12
44
|
'@rs-x/state-manager',
|
|
@@ -119,7 +151,7 @@ function parseArgs(argv) {
|
|
|
119
151
|
}
|
|
120
152
|
|
|
121
153
|
function run(command, args, options = {}) {
|
|
122
|
-
const { dryRun, cwd = process.cwd() } = options;
|
|
154
|
+
const { dryRun, cwd = process.cwd(), env } = options;
|
|
123
155
|
const printable = [command, ...args].join(' ');
|
|
124
156
|
|
|
125
157
|
if (dryRun) {
|
|
@@ -129,6 +161,7 @@ function run(command, args, options = {}) {
|
|
|
129
161
|
|
|
130
162
|
const result = spawnSync(command, args, {
|
|
131
163
|
cwd,
|
|
164
|
+
env: env ? { ...process.env, ...env } : process.env,
|
|
132
165
|
stdio: 'inherit',
|
|
133
166
|
});
|
|
134
167
|
|
|
@@ -191,11 +224,33 @@ function applyTagToPackages(packages, tag) {
|
|
|
191
224
|
}
|
|
192
225
|
|
|
193
226
|
function resolveInstallTag(flags) {
|
|
194
|
-
|
|
227
|
+
if (parseBooleanFlag(flags.next, false)) {
|
|
228
|
+
return 'next';
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (CLI_VERSION.includes('-')) {
|
|
232
|
+
return 'next';
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const checkoutRoot = findRepoRoot(__dirname);
|
|
236
|
+
if (!checkoutRoot) {
|
|
237
|
+
return undefined;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const branchResult = spawnSync('git', ['branch', '--show-current'], {
|
|
241
|
+
cwd: checkoutRoot,
|
|
242
|
+
encoding: 'utf8',
|
|
243
|
+
});
|
|
244
|
+
const branch = branchResult.status === 0 ? branchResult.stdout.trim() : '';
|
|
245
|
+
if (branch && branch !== 'main') {
|
|
246
|
+
return 'next';
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return undefined;
|
|
195
250
|
}
|
|
196
251
|
|
|
197
252
|
function installPackages(pm, packages, options = {}) {
|
|
198
|
-
const { dev = false, dryRun = false, label = 'packages', tag } = options;
|
|
253
|
+
const { dev = false, dryRun = false, label = 'packages', tag, cwd } = options;
|
|
199
254
|
const resolvedPackages = tag ? applyTagToPackages(packages, tag) : packages;
|
|
200
255
|
const argsByPm = {
|
|
201
256
|
pnpm: dev
|
|
@@ -220,24 +275,74 @@ function installPackages(pm, packages, options = {}) {
|
|
|
220
275
|
|
|
221
276
|
const tagInfo = tag ? ` (tag: ${tag})` : '';
|
|
222
277
|
logInfo(`Installing ${label} with ${pm}${tagInfo}...`);
|
|
223
|
-
run(pm, installArgs, { dryRun });
|
|
278
|
+
run(pm, installArgs, { dryRun, cwd });
|
|
224
279
|
logOk(`Installed ${label}.`);
|
|
225
280
|
}
|
|
226
281
|
|
|
227
|
-
function
|
|
228
|
-
|
|
282
|
+
function resolveLocalRsxSpecs(projectRoot, flags, options = {}) {
|
|
283
|
+
const tarballsDir =
|
|
284
|
+
typeof flags?.['tarballs-dir'] === 'string'
|
|
285
|
+
? path.resolve(projectRoot, flags['tarballs-dir'])
|
|
286
|
+
: typeof process.env.RSX_TARBALLS_DIR === 'string' &&
|
|
287
|
+
process.env.RSX_TARBALLS_DIR.trim().length > 0
|
|
288
|
+
? path.resolve(projectRoot, process.env.RSX_TARBALLS_DIR)
|
|
289
|
+
: null;
|
|
290
|
+
const workspaceRoot = findRepoRoot(projectRoot);
|
|
291
|
+
return resolveProjectRsxSpecs(
|
|
292
|
+
projectRoot,
|
|
293
|
+
workspaceRoot,
|
|
294
|
+
tarballsDir,
|
|
295
|
+
options,
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function installResolvedPackages(pm, packageNames, options = {}) {
|
|
300
|
+
const {
|
|
301
|
+
dryRun = false,
|
|
302
|
+
label = 'packages',
|
|
303
|
+
tag,
|
|
304
|
+
cwd,
|
|
305
|
+
specs,
|
|
306
|
+
dev = false,
|
|
307
|
+
} = options;
|
|
308
|
+
const resolvedPackages = packageNames.map((packageName) => {
|
|
309
|
+
const spec = specs?.[packageName];
|
|
310
|
+
return spec ? `${packageName}@${spec}` : packageName;
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
installPackages(pm, resolvedPackages, {
|
|
314
|
+
dev,
|
|
315
|
+
dryRun,
|
|
316
|
+
label,
|
|
317
|
+
tag: specs ? undefined : tag,
|
|
318
|
+
cwd,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function installRuntimePackages(pm, dryRun, tag, projectRoot, flags) {
|
|
323
|
+
const specs = resolveLocalRsxSpecs(projectRoot ?? process.cwd(), flags, {
|
|
324
|
+
tag,
|
|
325
|
+
});
|
|
326
|
+
installResolvedPackages(pm, RUNTIME_PACKAGES, {
|
|
229
327
|
dev: false,
|
|
230
328
|
dryRun,
|
|
231
329
|
tag,
|
|
330
|
+
specs,
|
|
331
|
+
cwd: projectRoot,
|
|
232
332
|
label: 'runtime RS-X packages',
|
|
233
333
|
});
|
|
234
334
|
}
|
|
235
335
|
|
|
236
|
-
function installCompilerPackages(pm, dryRun, tag) {
|
|
237
|
-
|
|
336
|
+
function installCompilerPackages(pm, dryRun, tag, projectRoot, flags) {
|
|
337
|
+
const specs = resolveLocalRsxSpecs(projectRoot ?? process.cwd(), flags, {
|
|
338
|
+
tag,
|
|
339
|
+
});
|
|
340
|
+
installResolvedPackages(pm, COMPILER_PACKAGES, {
|
|
238
341
|
dev: true,
|
|
239
342
|
dryRun,
|
|
240
343
|
tag,
|
|
344
|
+
specs,
|
|
345
|
+
cwd: projectRoot,
|
|
241
346
|
label: 'compiler tooling',
|
|
242
347
|
});
|
|
243
348
|
}
|
|
@@ -262,14 +367,50 @@ function installVsCodeExtension(flags) {
|
|
|
262
367
|
return;
|
|
263
368
|
}
|
|
264
369
|
|
|
265
|
-
|
|
370
|
+
installBundledVsix(dryRun, force);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function resolveBundledVsix() {
|
|
374
|
+
const packageRoot = path.resolve(__dirname, '..');
|
|
375
|
+
const candidates = fs
|
|
376
|
+
.readdirSync(packageRoot)
|
|
377
|
+
.filter((name) => /^rs-x-vscode-extension-.*\.vsix$/u.test(name))
|
|
378
|
+
.map((name) => path.join(packageRoot, name));
|
|
379
|
+
|
|
380
|
+
if (candidates.length === 0) {
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const latest = candidates
|
|
385
|
+
.map((fullPath) => ({
|
|
386
|
+
fullPath,
|
|
387
|
+
mtimeMs: fs.statSync(fullPath).mtimeMs,
|
|
388
|
+
}))
|
|
389
|
+
.sort((a, b) => b.mtimeMs - a.mtimeMs)[0];
|
|
390
|
+
|
|
391
|
+
return latest?.fullPath ?? null;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function installBundledVsix(dryRun, force) {
|
|
395
|
+
const bundledVsix = resolveBundledVsix();
|
|
396
|
+
if (!bundledVsix) {
|
|
397
|
+
logWarn(
|
|
398
|
+
'No bundled VSIX found in @rs-x/cli. Skipping VS Code extension install.',
|
|
399
|
+
);
|
|
400
|
+
logInfo(
|
|
401
|
+
'If you are developing in the rs-x repo, use `rsx install vscode --local` instead.',
|
|
402
|
+
);
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const args = ['--install-extension', bundledVsix];
|
|
266
407
|
if (force) {
|
|
267
408
|
args.push('--force');
|
|
268
409
|
}
|
|
269
410
|
|
|
270
|
-
logInfo(`Installing ${
|
|
411
|
+
logInfo(`Installing bundled VSIX from ${bundledVsix}...`);
|
|
271
412
|
run('code', args, { dryRun });
|
|
272
|
-
logOk('VS Code extension installed.');
|
|
413
|
+
logOk('VS Code extension installed from bundled VSIX.');
|
|
273
414
|
}
|
|
274
415
|
|
|
275
416
|
function installLocalVsix(dryRun, force) {
|
|
@@ -585,6 +726,210 @@ function writeFileWithDryRun(filePath, content, dryRun) {
|
|
|
585
726
|
fs.writeFileSync(filePath, content, 'utf8');
|
|
586
727
|
}
|
|
587
728
|
|
|
729
|
+
function stripJsonComments(content) {
|
|
730
|
+
let result = '';
|
|
731
|
+
let inString = false;
|
|
732
|
+
let stringDelimiter = '"';
|
|
733
|
+
let inLineComment = false;
|
|
734
|
+
let inBlockComment = false;
|
|
735
|
+
|
|
736
|
+
for (let index = 0; index < content.length; index += 1) {
|
|
737
|
+
const current = content[index];
|
|
738
|
+
const next = content[index + 1];
|
|
739
|
+
|
|
740
|
+
if (inLineComment) {
|
|
741
|
+
if (current === '\n') {
|
|
742
|
+
inLineComment = false;
|
|
743
|
+
result += current;
|
|
744
|
+
}
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
if (inBlockComment) {
|
|
749
|
+
if (current === '*' && next === '/') {
|
|
750
|
+
inBlockComment = false;
|
|
751
|
+
index += 1;
|
|
752
|
+
}
|
|
753
|
+
continue;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
if (inString) {
|
|
757
|
+
result += current;
|
|
758
|
+
if (current === '\\') {
|
|
759
|
+
index += 1;
|
|
760
|
+
if (index < content.length) {
|
|
761
|
+
result += content[index];
|
|
762
|
+
}
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
if (current === stringDelimiter) {
|
|
766
|
+
inString = false;
|
|
767
|
+
}
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
if (current === '"' || current === "'") {
|
|
772
|
+
inString = true;
|
|
773
|
+
stringDelimiter = current;
|
|
774
|
+
result += current;
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
if (current === '/' && next === '/') {
|
|
779
|
+
inLineComment = true;
|
|
780
|
+
index += 1;
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
if (current === '/' && next === '*') {
|
|
785
|
+
inBlockComment = true;
|
|
786
|
+
index += 1;
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
result += current;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
return result;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
function parseJsonc(content) {
|
|
797
|
+
return JSON.parse(stripJsonComments(content.replace(/^\uFEFF/u, '')));
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
function copyPathWithDryRun(sourcePath, targetPath, dryRun) {
|
|
801
|
+
if (dryRun) {
|
|
802
|
+
logInfo(`[dry-run] copy ${sourcePath} -> ${targetPath}`);
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
const stat = fs.statSync(sourcePath);
|
|
807
|
+
if (stat.isDirectory()) {
|
|
808
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
809
|
+
for (const entry of fs.readdirSync(sourcePath, { withFileTypes: true })) {
|
|
810
|
+
copyPathWithDryRun(
|
|
811
|
+
path.join(sourcePath, entry.name),
|
|
812
|
+
path.join(targetPath, entry.name),
|
|
813
|
+
false,
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
820
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function removeFileOrDirectoryWithDryRun(targetPath, dryRun) {
|
|
824
|
+
if (!fs.existsSync(targetPath)) {
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
if (dryRun) {
|
|
829
|
+
logInfo(`[dry-run] remove ${targetPath}`);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
function resolveAngularProjectTsConfig(projectRoot) {
|
|
837
|
+
const appTsConfigPath = path.join(projectRoot, 'tsconfig.app.json');
|
|
838
|
+
if (fs.existsSync(appTsConfigPath)) {
|
|
839
|
+
return appTsConfigPath;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
return path.join(projectRoot, 'tsconfig.json');
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
function upsertTypescriptPluginInTsConfig(configPath, dryRun) {
|
|
846
|
+
if (!fs.existsSync(configPath)) {
|
|
847
|
+
logWarn(`TypeScript config not found: ${configPath}`);
|
|
848
|
+
return;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
const tsConfig = parseJsonc(fs.readFileSync(configPath, 'utf8'));
|
|
852
|
+
const compilerOptions = tsConfig.compilerOptions ?? {};
|
|
853
|
+
const plugins = Array.isArray(compilerOptions.plugins)
|
|
854
|
+
? compilerOptions.plugins
|
|
855
|
+
: [];
|
|
856
|
+
|
|
857
|
+
if (
|
|
858
|
+
!plugins.some(
|
|
859
|
+
(plugin) =>
|
|
860
|
+
plugin &&
|
|
861
|
+
typeof plugin === 'object' &&
|
|
862
|
+
plugin.name === '@rs-x/typescript-plugin',
|
|
863
|
+
)
|
|
864
|
+
) {
|
|
865
|
+
plugins.push({ name: '@rs-x/typescript-plugin' });
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
compilerOptions.plugins = plugins;
|
|
869
|
+
tsConfig.compilerOptions = compilerOptions;
|
|
870
|
+
|
|
871
|
+
if (dryRun) {
|
|
872
|
+
logInfo(`[dry-run] patch ${configPath}`);
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
fs.writeFileSync(
|
|
877
|
+
configPath,
|
|
878
|
+
`${JSON.stringify(tsConfig, null, 2)}\n`,
|
|
879
|
+
'utf8',
|
|
880
|
+
);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
function ensureTsConfigIncludePattern(configPath, pattern, dryRun) {
|
|
884
|
+
if (!fs.existsSync(configPath)) {
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
const tsConfig = parseJsonc(fs.readFileSync(configPath, 'utf8'));
|
|
889
|
+
const include = Array.isArray(tsConfig.include) ? tsConfig.include : [];
|
|
890
|
+
if (!include.includes(pattern)) {
|
|
891
|
+
include.push(pattern);
|
|
892
|
+
}
|
|
893
|
+
tsConfig.include = include;
|
|
894
|
+
|
|
895
|
+
if (dryRun) {
|
|
896
|
+
logInfo(`[dry-run] patch ${configPath}`);
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
fs.writeFileSync(
|
|
901
|
+
configPath,
|
|
902
|
+
`${JSON.stringify(tsConfig, null, 2)}\n`,
|
|
903
|
+
'utf8',
|
|
904
|
+
);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
function ensureVueEnvTypes(projectRoot, dryRun) {
|
|
908
|
+
const envTypesPath = path.join(projectRoot, 'src', 'vite-env.d.ts');
|
|
909
|
+
const envTypesSource = `/// <reference types="vite/client" />
|
|
910
|
+
|
|
911
|
+
declare module '*.vue' {
|
|
912
|
+
import type { DefineComponent } from 'vue';
|
|
913
|
+
|
|
914
|
+
const component: DefineComponent<{}, {}, any>;
|
|
915
|
+
export default component;
|
|
916
|
+
}
|
|
917
|
+
`;
|
|
918
|
+
|
|
919
|
+
if (fs.existsSync(envTypesPath)) {
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
if (dryRun) {
|
|
924
|
+
logInfo(`[dry-run] create ${envTypesPath}`);
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
fs.mkdirSync(path.dirname(envTypesPath), { recursive: true });
|
|
929
|
+
fs.writeFileSync(envTypesPath, envTypesSource, 'utf8');
|
|
930
|
+
logOk(`Created ${envTypesPath}`);
|
|
931
|
+
}
|
|
932
|
+
|
|
588
933
|
function toFileDependencySpec(fromDir, targetPath) {
|
|
589
934
|
const relative = path.relative(fromDir, targetPath).replace(/\\/gu, '/');
|
|
590
935
|
const normalized = relative.startsWith('.') ? relative : `./${relative}`;
|
|
@@ -634,6 +979,8 @@ function resolveProjectRsxSpecs(
|
|
|
634
979
|
options = {},
|
|
635
980
|
) {
|
|
636
981
|
const includeAngularPackage = Boolean(options.includeAngularPackage);
|
|
982
|
+
const includeReactPackage = Boolean(options.includeReactPackage);
|
|
983
|
+
const includeVuePackage = Boolean(options.includeVuePackage);
|
|
637
984
|
const versionSpec = options.tag ? options.tag : RSX_PACKAGE_VERSION;
|
|
638
985
|
const defaults = {
|
|
639
986
|
'@rs-x/core': versionSpec,
|
|
@@ -642,7 +989,9 @@ function resolveProjectRsxSpecs(
|
|
|
642
989
|
'@rs-x/compiler': versionSpec,
|
|
643
990
|
'@rs-x/typescript-plugin': versionSpec,
|
|
644
991
|
...(includeAngularPackage ? { '@rs-x/angular': versionSpec } : {}),
|
|
645
|
-
'@rs-x/
|
|
992
|
+
...(includeReactPackage ? { '@rs-x/react': versionSpec } : {}),
|
|
993
|
+
...(includeVuePackage ? { '@rs-x/vue': versionSpec } : {}),
|
|
994
|
+
'@rs-x/cli': versionSpec,
|
|
646
995
|
};
|
|
647
996
|
|
|
648
997
|
const tarballSlugs = {
|
|
@@ -652,6 +1001,8 @@ function resolveProjectRsxSpecs(
|
|
|
652
1001
|
'@rs-x/compiler': 'rs-x-compiler',
|
|
653
1002
|
'@rs-x/typescript-plugin': 'rs-x-typescript-plugin',
|
|
654
1003
|
...(includeAngularPackage ? { '@rs-x/angular': 'rs-x-angular' } : {}),
|
|
1004
|
+
...(includeReactPackage ? { '@rs-x/react': 'rs-x-react' } : {}),
|
|
1005
|
+
...(includeVuePackage ? { '@rs-x/vue': 'rs-x-vue' } : {}),
|
|
655
1006
|
'@rs-x/cli': 'rs-x-cli',
|
|
656
1007
|
};
|
|
657
1008
|
|
|
@@ -674,6 +1025,16 @@ function resolveProjectRsxSpecs(
|
|
|
674
1025
|
'rs-x-angular': path.join(tarballsDir, 'rs-x-angular'),
|
|
675
1026
|
}
|
|
676
1027
|
: {}),
|
|
1028
|
+
...(includeReactPackage
|
|
1029
|
+
? {
|
|
1030
|
+
'rs-x-react': path.join(tarballsDir, 'rs-x-react'),
|
|
1031
|
+
}
|
|
1032
|
+
: {}),
|
|
1033
|
+
...(includeVuePackage
|
|
1034
|
+
? {
|
|
1035
|
+
'rs-x-vue': path.join(tarballsDir, 'rs-x-vue'),
|
|
1036
|
+
}
|
|
1037
|
+
: {}),
|
|
677
1038
|
'rs-x-cli': path.join(tarballsDir, 'rs-x-cli'),
|
|
678
1039
|
};
|
|
679
1040
|
|
|
@@ -711,10 +1072,21 @@ function resolveProjectRsxSpecs(
|
|
|
711
1072
|
),
|
|
712
1073
|
...(includeAngularPackage
|
|
713
1074
|
? {
|
|
714
|
-
'@rs-x/angular':
|
|
715
|
-
workspaceRoot,
|
|
716
|
-
|
|
717
|
-
|
|
1075
|
+
'@rs-x/angular': fs.existsSync(
|
|
1076
|
+
path.join(workspaceRoot, 'rs-x-angular/dist/rsx'),
|
|
1077
|
+
)
|
|
1078
|
+
? path.join(workspaceRoot, 'rs-x-angular/dist/rsx')
|
|
1079
|
+
: path.join(workspaceRoot, 'rs-x-angular/projects/rsx'),
|
|
1080
|
+
}
|
|
1081
|
+
: {}),
|
|
1082
|
+
...(includeReactPackage
|
|
1083
|
+
? {
|
|
1084
|
+
'@rs-x/react': path.join(workspaceRoot, 'rs-x-react'),
|
|
1085
|
+
}
|
|
1086
|
+
: {}),
|
|
1087
|
+
...(includeVuePackage
|
|
1088
|
+
? {
|
|
1089
|
+
'@rs-x/vue': path.join(workspaceRoot, 'rs-x-vue'),
|
|
718
1090
|
}
|
|
719
1091
|
: {}),
|
|
720
1092
|
'@rs-x/cli': path.join(workspaceRoot, 'rs-x-cli'),
|
|
@@ -777,6 +1149,22 @@ function createProjectPackageJson(projectName, rsxSpecs) {
|
|
|
777
1149
|
);
|
|
778
1150
|
}
|
|
779
1151
|
|
|
1152
|
+
function resolveProjectRoot(projectName, flags) {
|
|
1153
|
+
const parentDir =
|
|
1154
|
+
typeof flags?.['project-parent-dir'] === 'string'
|
|
1155
|
+
? flags['project-parent-dir']
|
|
1156
|
+
: typeof process.env.RSX_PROJECT_PARENT_DIR === 'string' &&
|
|
1157
|
+
process.env.RSX_PROJECT_PARENT_DIR.trim().length > 0
|
|
1158
|
+
? process.env.RSX_PROJECT_PARENT_DIR
|
|
1159
|
+
: null;
|
|
1160
|
+
|
|
1161
|
+
if (parentDir) {
|
|
1162
|
+
return path.resolve(parentDir, projectName);
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
return path.resolve(process.cwd(), projectName);
|
|
1166
|
+
}
|
|
1167
|
+
|
|
780
1168
|
function createProjectTsConfig() {
|
|
781
1169
|
return (
|
|
782
1170
|
JSON.stringify(
|
|
@@ -961,7 +1349,7 @@ async function runProject(flags) {
|
|
|
961
1349
|
}
|
|
962
1350
|
}
|
|
963
1351
|
|
|
964
|
-
const projectRoot =
|
|
1352
|
+
const projectRoot = resolveProjectRoot(projectName, flags);
|
|
965
1353
|
const tarballsDir =
|
|
966
1354
|
typeof flags['tarballs-dir'] === 'string'
|
|
967
1355
|
? path.resolve(process.cwd(), flags['tarballs-dir'])
|
|
@@ -1115,9 +1503,24 @@ async function resolveProjectName(nameFromFlags, fallbackName) {
|
|
|
1115
1503
|
}
|
|
1116
1504
|
}
|
|
1117
1505
|
|
|
1118
|
-
function scaffoldProjectTemplate(
|
|
1506
|
+
function scaffoldProjectTemplate(
|
|
1507
|
+
template,
|
|
1508
|
+
projectName,
|
|
1509
|
+
projectRoot,
|
|
1510
|
+
pm,
|
|
1511
|
+
flags,
|
|
1512
|
+
) {
|
|
1119
1513
|
const dryRun = Boolean(flags['dry-run']);
|
|
1120
1514
|
const skipInstall = Boolean(flags['skip-install']);
|
|
1515
|
+
const scaffoldCwd = path.dirname(projectRoot);
|
|
1516
|
+
const scaffoldProjectArg = `./${projectName}`;
|
|
1517
|
+
const scaffoldEnv = {
|
|
1518
|
+
INIT_CWD: scaffoldCwd,
|
|
1519
|
+
npm_config_local_prefix: scaffoldCwd,
|
|
1520
|
+
npm_prefix: scaffoldCwd,
|
|
1521
|
+
PWD: scaffoldCwd,
|
|
1522
|
+
CI: 'true',
|
|
1523
|
+
};
|
|
1121
1524
|
|
|
1122
1525
|
if (template === 'angular') {
|
|
1123
1526
|
const args = [
|
|
@@ -1125,6 +1528,8 @@ function scaffoldProjectTemplate(template, projectName, pm, flags) {
|
|
|
1125
1528
|
'@angular/cli@latest',
|
|
1126
1529
|
'new',
|
|
1127
1530
|
projectName,
|
|
1531
|
+
'--directory',
|
|
1532
|
+
scaffoldProjectArg,
|
|
1128
1533
|
'--defaults',
|
|
1129
1534
|
'--standalone',
|
|
1130
1535
|
'--routing',
|
|
@@ -1135,21 +1540,45 @@ function scaffoldProjectTemplate(template, projectName, pm, flags) {
|
|
|
1135
1540
|
if (skipInstall) {
|
|
1136
1541
|
args.push('--skip-install');
|
|
1137
1542
|
}
|
|
1138
|
-
run('npx', args, { dryRun });
|
|
1543
|
+
run('npx', args, { dryRun, cwd: scaffoldCwd, env: scaffoldEnv });
|
|
1139
1544
|
return;
|
|
1140
1545
|
}
|
|
1141
1546
|
|
|
1142
1547
|
if (template === 'react') {
|
|
1143
|
-
run(
|
|
1144
|
-
|
|
1145
|
-
|
|
1548
|
+
run(
|
|
1549
|
+
'npx',
|
|
1550
|
+
[
|
|
1551
|
+
'create-vite@latest',
|
|
1552
|
+
scaffoldProjectArg,
|
|
1553
|
+
'--no-interactive',
|
|
1554
|
+
'--template',
|
|
1555
|
+
'react-ts',
|
|
1556
|
+
],
|
|
1557
|
+
{
|
|
1558
|
+
dryRun,
|
|
1559
|
+
cwd: scaffoldCwd,
|
|
1560
|
+
env: scaffoldEnv,
|
|
1561
|
+
},
|
|
1562
|
+
);
|
|
1146
1563
|
return;
|
|
1147
1564
|
}
|
|
1148
1565
|
|
|
1149
1566
|
if (template === 'vuejs') {
|
|
1150
|
-
run(
|
|
1151
|
-
|
|
1152
|
-
|
|
1567
|
+
run(
|
|
1568
|
+
'npx',
|
|
1569
|
+
[
|
|
1570
|
+
'create-vite@latest',
|
|
1571
|
+
scaffoldProjectArg,
|
|
1572
|
+
'--no-interactive',
|
|
1573
|
+
'--template',
|
|
1574
|
+
'vue-ts',
|
|
1575
|
+
],
|
|
1576
|
+
{
|
|
1577
|
+
dryRun,
|
|
1578
|
+
cwd: scaffoldCwd,
|
|
1579
|
+
env: scaffoldEnv,
|
|
1580
|
+
},
|
|
1581
|
+
);
|
|
1153
1582
|
return;
|
|
1154
1583
|
}
|
|
1155
1584
|
|
|
@@ -1162,7 +1591,7 @@ function scaffoldProjectTemplate(template, projectName, pm, flags) {
|
|
|
1162
1591
|
};
|
|
1163
1592
|
const args = [
|
|
1164
1593
|
'create-next-app@latest',
|
|
1165
|
-
|
|
1594
|
+
scaffoldProjectArg,
|
|
1166
1595
|
'--yes',
|
|
1167
1596
|
'--ts',
|
|
1168
1597
|
'--app',
|
|
@@ -1174,7 +1603,7 @@ function scaffoldProjectTemplate(template, projectName, pm, flags) {
|
|
|
1174
1603
|
if (skipInstall) {
|
|
1175
1604
|
args.push('--skip-install');
|
|
1176
1605
|
}
|
|
1177
|
-
run('npx', args, { dryRun });
|
|
1606
|
+
run('npx', args, { dryRun, cwd: scaffoldCwd, env: scaffoldEnv });
|
|
1178
1607
|
return;
|
|
1179
1608
|
}
|
|
1180
1609
|
|
|
@@ -1182,57 +1611,589 @@ function scaffoldProjectTemplate(template, projectName, pm, flags) {
|
|
|
1182
1611
|
process.exit(1);
|
|
1183
1612
|
}
|
|
1184
1613
|
|
|
1185
|
-
|
|
1186
|
-
const
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1614
|
+
function applyAngularDemoStarter(projectRoot, projectName, pm, flags) {
|
|
1615
|
+
const dryRun = Boolean(flags['dry-run']);
|
|
1616
|
+
const tag = resolveInstallTag(flags);
|
|
1617
|
+
const tarballsDir =
|
|
1618
|
+
typeof flags['tarballs-dir'] === 'string'
|
|
1619
|
+
? path.resolve(process.cwd(), flags['tarballs-dir'])
|
|
1620
|
+
: typeof process.env.RSX_TARBALLS_DIR === 'string' &&
|
|
1621
|
+
process.env.RSX_TARBALLS_DIR.trim().length > 0
|
|
1622
|
+
? path.resolve(process.cwd(), process.env.RSX_TARBALLS_DIR)
|
|
1623
|
+
: null;
|
|
1624
|
+
const workspaceRoot = findRepoRoot(projectRoot);
|
|
1625
|
+
const rsxSpecs = resolveProjectRsxSpecs(
|
|
1626
|
+
projectRoot,
|
|
1627
|
+
workspaceRoot,
|
|
1628
|
+
tarballsDir,
|
|
1629
|
+
{ tag, includeAngularPackage: true },
|
|
1630
|
+
);
|
|
1631
|
+
|
|
1632
|
+
const templateFiles = ['README.md', 'src'];
|
|
1633
|
+
for (const entry of templateFiles) {
|
|
1634
|
+
copyPathWithDryRun(
|
|
1635
|
+
path.join(ANGULAR_DEMO_TEMPLATE_DIR, entry),
|
|
1636
|
+
path.join(projectRoot, entry),
|
|
1637
|
+
dryRun,
|
|
1190
1638
|
);
|
|
1191
|
-
process.exit(1);
|
|
1192
1639
|
}
|
|
1193
1640
|
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1641
|
+
const staleAngularFiles = [
|
|
1642
|
+
path.join(projectRoot, 'src/app/app.ts'),
|
|
1643
|
+
path.join(projectRoot, 'src/app/app.spec.ts'),
|
|
1644
|
+
path.join(projectRoot, 'src/app/app.html'),
|
|
1645
|
+
path.join(projectRoot, 'src/app/app.css'),
|
|
1646
|
+
path.join(projectRoot, 'src/app/app.routes.ts'),
|
|
1647
|
+
path.join(projectRoot, 'src/app/app.config.ts'),
|
|
1648
|
+
];
|
|
1649
|
+
for (const stalePath of staleAngularFiles) {
|
|
1650
|
+
removeFileOrDirectoryWithDryRun(stalePath, dryRun);
|
|
1197
1651
|
}
|
|
1198
1652
|
|
|
1199
|
-
const
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1653
|
+
const readmePath = path.join(projectRoot, 'README.md');
|
|
1654
|
+
if (fs.existsSync(readmePath)) {
|
|
1655
|
+
const readmeSource = fs.readFileSync(readmePath, 'utf8');
|
|
1656
|
+
const nextReadme = readmeSource.replace(
|
|
1657
|
+
/^#\s+rsx-angular-example/mu,
|
|
1658
|
+
`# ${projectName}`,
|
|
1659
|
+
);
|
|
1660
|
+
if (dryRun) {
|
|
1661
|
+
logInfo(`[dry-run] patch ${readmePath}`);
|
|
1662
|
+
} else {
|
|
1663
|
+
fs.writeFileSync(readmePath, nextReadme, 'utf8');
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
1668
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
1669
|
+
logError(
|
|
1670
|
+
`package.json not found in generated Angular app: ${packageJsonPath}`,
|
|
1671
|
+
);
|
|
1204
1672
|
process.exit(1);
|
|
1205
1673
|
}
|
|
1206
1674
|
|
|
1207
|
-
|
|
1208
|
-
const
|
|
1675
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
1676
|
+
const angularTsConfigPath = resolveAngularProjectTsConfig(projectRoot);
|
|
1677
|
+
const angularTsConfigRelative = path
|
|
1678
|
+
.relative(projectRoot, angularTsConfigPath)
|
|
1679
|
+
.replace(/\\/gu, '/');
|
|
1680
|
+
packageJson.name = projectName;
|
|
1681
|
+
packageJson.private = true;
|
|
1682
|
+
packageJson.version = '0.1.0';
|
|
1683
|
+
packageJson.scripts = {
|
|
1684
|
+
'build:rsx': `rsx build --project ${angularTsConfigRelative} --no-emit --prod`,
|
|
1685
|
+
'typecheck:rsx': `rsx typecheck --project ${angularTsConfigRelative}`,
|
|
1686
|
+
prebuild: 'npm run build:rsx',
|
|
1687
|
+
start: 'npm run build:rsx && ng serve',
|
|
1688
|
+
build: 'ng build',
|
|
1689
|
+
};
|
|
1690
|
+
packageJson.rsx = {
|
|
1691
|
+
build: {
|
|
1692
|
+
preparse: true,
|
|
1693
|
+
preparseFile: 'src/rsx-generated/rsx-aot-preparsed.generated.ts',
|
|
1694
|
+
compiled: true,
|
|
1695
|
+
compiledFile: 'src/rsx-generated/rsx-aot-compiled.generated.ts',
|
|
1696
|
+
registrationFile: 'src/rsx-generated/rsx-aot-registration.generated.ts',
|
|
1697
|
+
compiledResolvedEvaluator: false,
|
|
1698
|
+
},
|
|
1699
|
+
};
|
|
1700
|
+
packageJson.dependencies = {
|
|
1701
|
+
...(packageJson.dependencies ?? {}),
|
|
1702
|
+
'@rs-x/angular': rsxSpecs['@rs-x/angular'],
|
|
1703
|
+
'@rs-x/core': rsxSpecs['@rs-x/core'],
|
|
1704
|
+
'@rs-x/state-manager': rsxSpecs['@rs-x/state-manager'],
|
|
1705
|
+
'@rs-x/expression-parser': rsxSpecs['@rs-x/expression-parser'],
|
|
1706
|
+
};
|
|
1707
|
+
packageJson.devDependencies = {
|
|
1708
|
+
...(packageJson.devDependencies ?? {}),
|
|
1709
|
+
'@rs-x/cli': rsxSpecs['@rs-x/cli'],
|
|
1710
|
+
'@rs-x/compiler': rsxSpecs['@rs-x/compiler'],
|
|
1711
|
+
'@rs-x/typescript-plugin': rsxSpecs['@rs-x/typescript-plugin'],
|
|
1712
|
+
};
|
|
1713
|
+
|
|
1209
1714
|
if (dryRun) {
|
|
1210
|
-
logInfo(`[dry-run]
|
|
1211
|
-
|
|
1715
|
+
logInfo(`[dry-run] patch ${packageJsonPath}`);
|
|
1716
|
+
} else {
|
|
1717
|
+
fs.writeFileSync(
|
|
1718
|
+
packageJsonPath,
|
|
1719
|
+
`${JSON.stringify(packageJson, null, 2)}\n`,
|
|
1720
|
+
'utf8',
|
|
1721
|
+
);
|
|
1212
1722
|
}
|
|
1213
1723
|
|
|
1214
|
-
|
|
1724
|
+
const angularJsonPath = path.join(projectRoot, 'angular.json');
|
|
1725
|
+
if (!fs.existsSync(angularJsonPath)) {
|
|
1726
|
+
logError(
|
|
1727
|
+
`angular.json not found in generated Angular app: ${angularJsonPath}`,
|
|
1728
|
+
);
|
|
1729
|
+
process.exit(1);
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
const angularJson = JSON.parse(fs.readFileSync(angularJsonPath, 'utf8'));
|
|
1733
|
+
const projects = angularJson.projects ?? {};
|
|
1734
|
+
const [angularProjectName] = Object.keys(projects);
|
|
1735
|
+
if (!angularProjectName) {
|
|
1736
|
+
logError('Generated angular.json does not define any projects.');
|
|
1737
|
+
process.exit(1);
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
const angularProject = projects[angularProjectName];
|
|
1741
|
+
const architect = angularProject.architect ?? angularProject.targets;
|
|
1742
|
+
const build = architect?.build;
|
|
1743
|
+
if (!build) {
|
|
1744
|
+
logError('Generated Angular project is missing a build target.');
|
|
1745
|
+
process.exit(1);
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
const buildOptions = build.options ?? {};
|
|
1749
|
+
const styles = Array.isArray(buildOptions.styles) ? buildOptions.styles : [];
|
|
1750
|
+
if (!styles.includes('src/styles.css')) {
|
|
1751
|
+
styles.push('src/styles.css');
|
|
1752
|
+
}
|
|
1753
|
+
buildOptions.styles = styles;
|
|
1754
|
+
buildOptions.preserveSymlinks = true;
|
|
1755
|
+
|
|
1756
|
+
const registrationFile =
|
|
1757
|
+
'src/rsx-generated/rsx-aot-registration.generated.ts';
|
|
1758
|
+
let polyfills = buildOptions.polyfills;
|
|
1759
|
+
if (typeof polyfills === 'string') {
|
|
1760
|
+
polyfills = [polyfills];
|
|
1761
|
+
} else if (!Array.isArray(polyfills)) {
|
|
1762
|
+
polyfills = [];
|
|
1763
|
+
}
|
|
1764
|
+
if (!polyfills.includes(registrationFile)) {
|
|
1765
|
+
polyfills.push(registrationFile);
|
|
1766
|
+
}
|
|
1767
|
+
buildOptions.polyfills = polyfills;
|
|
1768
|
+
build.options = buildOptions;
|
|
1769
|
+
|
|
1770
|
+
if (build.configurations?.production?.budgets) {
|
|
1771
|
+
delete build.configurations.production.budgets;
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
if (dryRun) {
|
|
1775
|
+
logInfo(`[dry-run] patch ${angularJsonPath}`);
|
|
1776
|
+
} else {
|
|
1777
|
+
fs.writeFileSync(
|
|
1778
|
+
angularJsonPath,
|
|
1779
|
+
`${JSON.stringify(angularJson, null, 2)}\n`,
|
|
1780
|
+
'utf8',
|
|
1781
|
+
);
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
if (!Boolean(flags['skip-install'])) {
|
|
1785
|
+
logInfo(`Refreshing ${pm} dependencies for the RS-X Angular starter...`);
|
|
1786
|
+
run(pm, ['install'], { dryRun });
|
|
1787
|
+
logOk('Angular starter dependencies are up to date.');
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
function applyReactDemoStarter(projectRoot, projectName, pm, flags) {
|
|
1792
|
+
const dryRun = Boolean(flags['dry-run']);
|
|
1793
|
+
const tag = resolveInstallTag(flags);
|
|
1794
|
+
const tarballsDir =
|
|
1795
|
+
typeof flags['tarballs-dir'] === 'string'
|
|
1796
|
+
? path.resolve(process.cwd(), flags['tarballs-dir'])
|
|
1797
|
+
: typeof process.env.RSX_TARBALLS_DIR === 'string' &&
|
|
1798
|
+
process.env.RSX_TARBALLS_DIR.trim().length > 0
|
|
1799
|
+
? path.resolve(process.cwd(), process.env.RSX_TARBALLS_DIR)
|
|
1800
|
+
: null;
|
|
1801
|
+
const workspaceRoot = findRepoRoot(projectRoot);
|
|
1802
|
+
const rsxSpecs = resolveProjectRsxSpecs(
|
|
1803
|
+
projectRoot,
|
|
1804
|
+
workspaceRoot,
|
|
1805
|
+
tarballsDir,
|
|
1806
|
+
{ tag, includeReactPackage: true },
|
|
1807
|
+
);
|
|
1808
|
+
|
|
1809
|
+
const templateFiles = [
|
|
1810
|
+
'README.md',
|
|
1811
|
+
'index.html',
|
|
1812
|
+
'src',
|
|
1813
|
+
'tsconfig.json',
|
|
1814
|
+
'vite.config.ts',
|
|
1815
|
+
];
|
|
1816
|
+
for (const entry of templateFiles) {
|
|
1817
|
+
copyPathWithDryRun(
|
|
1818
|
+
path.join(REACT_DEMO_TEMPLATE_DIR, entry),
|
|
1819
|
+
path.join(projectRoot, entry),
|
|
1820
|
+
dryRun,
|
|
1821
|
+
);
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
const staleReactFiles = [
|
|
1825
|
+
path.join(projectRoot, 'src/App.tsx'),
|
|
1826
|
+
path.join(projectRoot, 'src/App.css'),
|
|
1827
|
+
path.join(projectRoot, 'src/index.css'),
|
|
1828
|
+
path.join(projectRoot, 'src/vite-env.d.ts'),
|
|
1829
|
+
path.join(projectRoot, 'src/assets'),
|
|
1830
|
+
path.join(projectRoot, 'public'),
|
|
1831
|
+
path.join(projectRoot, 'eslint.config.js'),
|
|
1832
|
+
path.join(projectRoot, 'eslint.config.ts'),
|
|
1833
|
+
path.join(projectRoot, 'tsconfig.app.json'),
|
|
1834
|
+
path.join(projectRoot, 'tsconfig.node.json'),
|
|
1835
|
+
];
|
|
1836
|
+
for (const stalePath of staleReactFiles) {
|
|
1837
|
+
removeFileOrDirectoryWithDryRun(stalePath, dryRun);
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
const readmePath = path.join(projectRoot, 'README.md');
|
|
1841
|
+
if (fs.existsSync(readmePath)) {
|
|
1842
|
+
const readmeSource = fs.readFileSync(readmePath, 'utf8');
|
|
1843
|
+
const nextReadme = readmeSource.replace(
|
|
1844
|
+
/^#\s+rsx-react-example/mu,
|
|
1845
|
+
`# ${projectName}`,
|
|
1846
|
+
);
|
|
1847
|
+
if (dryRun) {
|
|
1848
|
+
logInfo(`[dry-run] patch ${readmePath}`);
|
|
1849
|
+
} else {
|
|
1850
|
+
fs.writeFileSync(readmePath, nextReadme, 'utf8');
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
1855
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
1856
|
+
logError(
|
|
1857
|
+
`package.json not found in generated React app: ${packageJsonPath}`,
|
|
1858
|
+
);
|
|
1859
|
+
process.exit(1);
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
1863
|
+
packageJson.name = projectName;
|
|
1864
|
+
packageJson.private = true;
|
|
1865
|
+
packageJson.version = '0.1.0';
|
|
1866
|
+
packageJson.type = 'module';
|
|
1867
|
+
packageJson.scripts = {
|
|
1868
|
+
'build:rsx': 'rsx build --project tsconfig.json --no-emit --prod',
|
|
1869
|
+
dev: 'npm run build:rsx && vite',
|
|
1870
|
+
build: 'npm run build:rsx && vite build',
|
|
1871
|
+
preview: 'vite preview',
|
|
1872
|
+
};
|
|
1873
|
+
packageJson.rsx = {
|
|
1874
|
+
build: {
|
|
1875
|
+
preparse: true,
|
|
1876
|
+
preparseFile: 'src/rsx-generated/rsx-aot-preparsed.generated.ts',
|
|
1877
|
+
compiled: true,
|
|
1878
|
+
compiledFile: 'src/rsx-generated/rsx-aot-compiled.generated.ts',
|
|
1879
|
+
compiledResolvedEvaluator: false,
|
|
1880
|
+
},
|
|
1881
|
+
};
|
|
1882
|
+
packageJson.dependencies = {
|
|
1883
|
+
react: packageJson.dependencies?.react ?? '^19.2.4',
|
|
1884
|
+
'react-dom': packageJson.dependencies?.['react-dom'] ?? '^19.2.4',
|
|
1885
|
+
'@rs-x/core': rsxSpecs['@rs-x/core'],
|
|
1886
|
+
'@rs-x/state-manager': rsxSpecs['@rs-x/state-manager'],
|
|
1887
|
+
'@rs-x/expression-parser': rsxSpecs['@rs-x/expression-parser'],
|
|
1888
|
+
'@rs-x/react': rsxSpecs['@rs-x/react'],
|
|
1889
|
+
};
|
|
1890
|
+
packageJson.devDependencies = {
|
|
1891
|
+
typescript: packageJson.devDependencies?.typescript ?? '^5.9.3',
|
|
1892
|
+
vite: packageJson.devDependencies?.vite ?? '^7.3.1',
|
|
1893
|
+
'@vitejs/plugin-react':
|
|
1894
|
+
packageJson.devDependencies?.['@vitejs/plugin-react'] ?? '^5.1.4',
|
|
1895
|
+
'@types/react': packageJson.devDependencies?.['@types/react'] ?? '^19.2.2',
|
|
1896
|
+
'@types/react-dom':
|
|
1897
|
+
packageJson.devDependencies?.['@types/react-dom'] ?? '^19.2.2',
|
|
1898
|
+
'@rs-x/cli': rsxSpecs['@rs-x/cli'],
|
|
1899
|
+
'@rs-x/compiler': rsxSpecs['@rs-x/compiler'],
|
|
1900
|
+
'@rs-x/typescript-plugin': rsxSpecs['@rs-x/typescript-plugin'],
|
|
1901
|
+
};
|
|
1902
|
+
|
|
1903
|
+
if (dryRun) {
|
|
1904
|
+
logInfo(`[dry-run] patch ${packageJsonPath}`);
|
|
1905
|
+
} else {
|
|
1906
|
+
fs.writeFileSync(
|
|
1907
|
+
packageJsonPath,
|
|
1908
|
+
`${JSON.stringify(packageJson, null, 2)}\n`,
|
|
1909
|
+
'utf8',
|
|
1910
|
+
);
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
const tsConfigPath = path.join(projectRoot, 'tsconfig.json');
|
|
1914
|
+
if (fs.existsSync(tsConfigPath)) {
|
|
1915
|
+
upsertTypescriptPluginInTsConfig(tsConfigPath, dryRun);
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
if (!Boolean(flags['skip-install'])) {
|
|
1919
|
+
logInfo(`Refreshing ${pm} dependencies for the RS-X React starter...`);
|
|
1920
|
+
run(pm, ['install'], { dryRun });
|
|
1921
|
+
logOk('React starter dependencies are up to date.');
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
function applyVueDemoStarter(projectRoot, projectName, pm, flags) {
|
|
1926
|
+
const dryRun = Boolean(flags['dry-run']);
|
|
1927
|
+
const tag = resolveInstallTag(flags);
|
|
1928
|
+
const tarballsDir =
|
|
1929
|
+
typeof flags['tarballs-dir'] === 'string'
|
|
1930
|
+
? path.resolve(process.cwd(), flags['tarballs-dir'])
|
|
1931
|
+
: typeof process.env.RSX_TARBALLS_DIR === 'string' &&
|
|
1932
|
+
process.env.RSX_TARBALLS_DIR.trim().length > 0
|
|
1933
|
+
? path.resolve(process.cwd(), process.env.RSX_TARBALLS_DIR)
|
|
1934
|
+
: null;
|
|
1935
|
+
const workspaceRoot = findRepoRoot(projectRoot);
|
|
1936
|
+
const rsxSpecs = resolveProjectRsxSpecs(
|
|
1937
|
+
projectRoot,
|
|
1938
|
+
workspaceRoot,
|
|
1939
|
+
tarballsDir,
|
|
1940
|
+
{ tag, includeVuePackage: true },
|
|
1941
|
+
);
|
|
1942
|
+
|
|
1943
|
+
const templateFiles = ['README.md', 'src'];
|
|
1944
|
+
for (const entry of templateFiles) {
|
|
1945
|
+
copyPathWithDryRun(
|
|
1946
|
+
path.join(VUE_DEMO_TEMPLATE_DIR, entry),
|
|
1947
|
+
path.join(projectRoot, entry),
|
|
1948
|
+
dryRun,
|
|
1949
|
+
);
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
const staleVueFiles = [
|
|
1953
|
+
path.join(projectRoot, 'public'),
|
|
1954
|
+
path.join(projectRoot, 'src/components/HelloWorld.vue'),
|
|
1955
|
+
path.join(projectRoot, 'src/assets'),
|
|
1956
|
+
];
|
|
1957
|
+
for (const stalePath of staleVueFiles) {
|
|
1958
|
+
removeFileOrDirectoryWithDryRun(stalePath, dryRun);
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
const readmePath = path.join(projectRoot, 'README.md');
|
|
1962
|
+
if (fs.existsSync(readmePath)) {
|
|
1963
|
+
const readmeSource = fs.readFileSync(readmePath, 'utf8');
|
|
1964
|
+
const nextReadme = readmeSource.replace(
|
|
1965
|
+
/^#\s+rsx-vue-example/mu,
|
|
1966
|
+
`# ${projectName}`,
|
|
1967
|
+
);
|
|
1968
|
+
if (dryRun) {
|
|
1969
|
+
logInfo(`[dry-run] patch ${readmePath}`);
|
|
1970
|
+
} else {
|
|
1971
|
+
fs.writeFileSync(readmePath, nextReadme, 'utf8');
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
1976
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
1977
|
+
logError(`package.json not found in generated Vue app: ${packageJsonPath}`);
|
|
1978
|
+
process.exit(1);
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
1982
|
+
packageJson.name = projectName;
|
|
1983
|
+
packageJson.private = true;
|
|
1984
|
+
packageJson.version = '0.1.0';
|
|
1985
|
+
packageJson.type = 'module';
|
|
1986
|
+
packageJson.scripts = {
|
|
1987
|
+
'build:rsx': 'rsx build --project tsconfig.app.json --no-emit --prod',
|
|
1988
|
+
'typecheck:rsx': 'rsx typecheck --project tsconfig.app.json',
|
|
1989
|
+
dev: 'npm run build:rsx && vite',
|
|
1990
|
+
build: 'npm run build:rsx && vue-tsc -b && vite build',
|
|
1991
|
+
preview: 'vite preview',
|
|
1992
|
+
};
|
|
1993
|
+
packageJson.rsx = {
|
|
1994
|
+
build: {
|
|
1995
|
+
preparse: true,
|
|
1996
|
+
preparseFile: 'src/rsx-generated/rsx-aot-preparsed.generated.ts',
|
|
1997
|
+
compiled: true,
|
|
1998
|
+
compiledFile: 'src/rsx-generated/rsx-aot-compiled.generated.ts',
|
|
1999
|
+
compiledResolvedEvaluator: false,
|
|
2000
|
+
},
|
|
2001
|
+
};
|
|
2002
|
+
packageJson.dependencies = {
|
|
2003
|
+
vue: packageJson.dependencies?.vue ?? '^3.5.30',
|
|
2004
|
+
'@rs-x/core': rsxSpecs['@rs-x/core'],
|
|
2005
|
+
'@rs-x/state-manager': rsxSpecs['@rs-x/state-manager'],
|
|
2006
|
+
'@rs-x/expression-parser': rsxSpecs['@rs-x/expression-parser'],
|
|
2007
|
+
'@rs-x/vue': rsxSpecs['@rs-x/vue'],
|
|
2008
|
+
};
|
|
2009
|
+
packageJson.devDependencies = {
|
|
2010
|
+
...(packageJson.devDependencies ?? {}),
|
|
2011
|
+
'@rs-x/cli': rsxSpecs['@rs-x/cli'],
|
|
2012
|
+
'@rs-x/compiler': rsxSpecs['@rs-x/compiler'],
|
|
2013
|
+
'@rs-x/typescript-plugin': rsxSpecs['@rs-x/typescript-plugin'],
|
|
2014
|
+
};
|
|
2015
|
+
|
|
2016
|
+
if (dryRun) {
|
|
2017
|
+
logInfo(`[dry-run] patch ${packageJsonPath}`);
|
|
2018
|
+
} else {
|
|
2019
|
+
fs.writeFileSync(
|
|
2020
|
+
packageJsonPath,
|
|
2021
|
+
`${JSON.stringify(packageJson, null, 2)}\n`,
|
|
2022
|
+
'utf8',
|
|
2023
|
+
);
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
const tsConfigAppPath = path.join(projectRoot, 'tsconfig.app.json');
|
|
2027
|
+
if (fs.existsSync(tsConfigAppPath)) {
|
|
2028
|
+
upsertTypescriptPluginInTsConfig(tsConfigAppPath, dryRun);
|
|
2029
|
+
ensureTsConfigIncludePattern(tsConfigAppPath, 'src/**/*.d.ts', dryRun);
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
if (!Boolean(flags['skip-install'])) {
|
|
2033
|
+
logInfo(`Refreshing ${pm} dependencies for the RS-X Vue starter...`);
|
|
2034
|
+
run(pm, ['install'], { dryRun });
|
|
2035
|
+
logOk('Vue starter dependencies are up to date.');
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
function applyNextDemoStarter(projectRoot, projectName, pm, flags) {
|
|
2040
|
+
const dryRun = Boolean(flags['dry-run']);
|
|
2041
|
+
const tag = resolveInstallTag(flags);
|
|
2042
|
+
const tarballsDir =
|
|
2043
|
+
typeof flags['tarballs-dir'] === 'string'
|
|
2044
|
+
? path.resolve(process.cwd(), flags['tarballs-dir'])
|
|
2045
|
+
: typeof process.env.RSX_TARBALLS_DIR === 'string' &&
|
|
2046
|
+
process.env.RSX_TARBALLS_DIR.trim().length > 0
|
|
2047
|
+
? path.resolve(process.cwd(), process.env.RSX_TARBALLS_DIR)
|
|
2048
|
+
: null;
|
|
2049
|
+
const workspaceRoot = findRepoRoot(projectRoot);
|
|
2050
|
+
const rsxSpecs = resolveProjectRsxSpecs(
|
|
2051
|
+
projectRoot,
|
|
2052
|
+
workspaceRoot,
|
|
2053
|
+
tarballsDir,
|
|
2054
|
+
{ tag, includeReactPackage: true },
|
|
2055
|
+
);
|
|
2056
|
+
|
|
2057
|
+
const templateFiles = ['README.md', 'app', 'components', 'hooks', 'lib'];
|
|
2058
|
+
for (const entry of templateFiles) {
|
|
2059
|
+
copyPathWithDryRun(
|
|
2060
|
+
path.join(NEXT_DEMO_TEMPLATE_DIR, entry),
|
|
2061
|
+
path.join(projectRoot, entry),
|
|
2062
|
+
dryRun,
|
|
2063
|
+
);
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
const readmePath = path.join(projectRoot, 'README.md');
|
|
2067
|
+
if (fs.existsSync(readmePath)) {
|
|
2068
|
+
const readmeSource = fs.readFileSync(readmePath, 'utf8');
|
|
2069
|
+
const nextReadme = readmeSource.replace(
|
|
2070
|
+
/^#\s+rsx-next-example/mu,
|
|
2071
|
+
`# ${projectName}`,
|
|
2072
|
+
);
|
|
2073
|
+
if (dryRun) {
|
|
2074
|
+
logInfo(`[dry-run] patch ${readmePath}`);
|
|
2075
|
+
} else {
|
|
2076
|
+
fs.writeFileSync(readmePath, nextReadme, 'utf8');
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
const publicDir = path.join(projectRoot, 'public');
|
|
2081
|
+
removeFileOrDirectoryWithDryRun(publicDir, dryRun);
|
|
2082
|
+
|
|
2083
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
2084
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
2085
|
+
logError(
|
|
2086
|
+
`package.json not found in generated Next.js app: ${packageJsonPath}`,
|
|
2087
|
+
);
|
|
2088
|
+
process.exit(1);
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
2092
|
+
packageJson.name = projectName;
|
|
2093
|
+
packageJson.private = true;
|
|
2094
|
+
packageJson.version = '0.1.0';
|
|
2095
|
+
packageJson.scripts = {
|
|
2096
|
+
...packageJson.scripts,
|
|
2097
|
+
'build:rsx': 'rsx build --project tsconfig.json --no-emit --prod',
|
|
2098
|
+
dev: 'npm run build:rsx && next dev',
|
|
2099
|
+
build: 'npm run build:rsx && next build',
|
|
2100
|
+
start: 'next start',
|
|
2101
|
+
};
|
|
2102
|
+
packageJson.rsx = {
|
|
2103
|
+
build: {
|
|
2104
|
+
preparse: true,
|
|
2105
|
+
preparseFile: 'app/rsx-generated/rsx-aot-preparsed.generated.ts',
|
|
2106
|
+
compiled: true,
|
|
2107
|
+
compiledFile: 'app/rsx-generated/rsx-aot-compiled.generated.ts',
|
|
2108
|
+
compiledResolvedEvaluator: false,
|
|
2109
|
+
},
|
|
2110
|
+
};
|
|
2111
|
+
packageJson.dependencies = {
|
|
2112
|
+
...(packageJson.dependencies ?? {}),
|
|
2113
|
+
'@rs-x/core': rsxSpecs['@rs-x/core'],
|
|
2114
|
+
'@rs-x/state-manager': rsxSpecs['@rs-x/state-manager'],
|
|
2115
|
+
'@rs-x/expression-parser': rsxSpecs['@rs-x/expression-parser'],
|
|
2116
|
+
'@rs-x/react': rsxSpecs['@rs-x/react'],
|
|
2117
|
+
};
|
|
2118
|
+
packageJson.devDependencies = {
|
|
2119
|
+
...(packageJson.devDependencies ?? {}),
|
|
2120
|
+
'@rs-x/cli': rsxSpecs['@rs-x/cli'],
|
|
2121
|
+
'@rs-x/compiler': rsxSpecs['@rs-x/compiler'],
|
|
2122
|
+
'@rs-x/typescript-plugin': rsxSpecs['@rs-x/typescript-plugin'],
|
|
2123
|
+
};
|
|
2124
|
+
|
|
2125
|
+
if (dryRun) {
|
|
2126
|
+
logInfo(`[dry-run] patch ${packageJsonPath}`);
|
|
2127
|
+
} else {
|
|
2128
|
+
fs.writeFileSync(
|
|
2129
|
+
packageJsonPath,
|
|
2130
|
+
`${JSON.stringify(packageJson, null, 2)}\n`,
|
|
2131
|
+
'utf8',
|
|
2132
|
+
);
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
const tsConfigPath = path.join(projectRoot, 'tsconfig.json');
|
|
2136
|
+
if (fs.existsSync(tsConfigPath)) {
|
|
2137
|
+
upsertTypescriptPluginInTsConfig(tsConfigPath, dryRun);
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
if (!Boolean(flags['skip-install'])) {
|
|
2141
|
+
logInfo(`Refreshing ${pm} dependencies for the RS-X Next.js starter...`);
|
|
2142
|
+
run(pm, ['install'], { dryRun });
|
|
2143
|
+
logOk('Next.js starter dependencies are up to date.');
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2147
|
+
async function runProjectWithTemplate(template, flags) {
|
|
2148
|
+
const normalizedTemplate = normalizeProjectTemplate(template);
|
|
2149
|
+
if (!normalizedTemplate) {
|
|
2150
|
+
logError(
|
|
2151
|
+
`Unsupported template '${template}'. Choose one of: ${PROJECT_TEMPLATES.join(', ')}`,
|
|
2152
|
+
);
|
|
2153
|
+
process.exit(1);
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
if (normalizedTemplate === 'nodejs') {
|
|
2157
|
+
await runProject(flags);
|
|
2158
|
+
return;
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
const pm = detectPackageManager(flags.pm);
|
|
2162
|
+
const projectName = await resolveProjectName(flags.name, flags._nameHint);
|
|
2163
|
+
const projectRoot = resolveProjectRoot(projectName, flags);
|
|
2164
|
+
if (fs.existsSync(projectRoot) && fs.readdirSync(projectRoot).length > 0) {
|
|
2165
|
+
logError(`Target directory is not empty: ${projectRoot}`);
|
|
2166
|
+
process.exit(1);
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
scaffoldProjectTemplate(
|
|
2170
|
+
normalizedTemplate,
|
|
2171
|
+
projectName,
|
|
2172
|
+
projectRoot,
|
|
2173
|
+
pm,
|
|
2174
|
+
flags,
|
|
2175
|
+
);
|
|
2176
|
+
const dryRun = Boolean(flags['dry-run']);
|
|
2177
|
+
if (dryRun) {
|
|
2178
|
+
logInfo(`[dry-run] setup RS-X in ${projectRoot}`);
|
|
2179
|
+
return;
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
withWorkingDirectory(projectRoot, () => {
|
|
1215
2183
|
if (normalizedTemplate === 'angular') {
|
|
1216
|
-
|
|
2184
|
+
applyAngularDemoStarter(projectRoot, projectName, pm, flags);
|
|
1217
2185
|
return;
|
|
1218
2186
|
}
|
|
1219
2187
|
if (normalizedTemplate === 'react') {
|
|
1220
|
-
|
|
1221
|
-
...flags,
|
|
1222
|
-
entry: flags.entry ?? 'src/main.tsx',
|
|
1223
|
-
});
|
|
2188
|
+
applyReactDemoStarter(projectRoot, projectName, pm, flags);
|
|
1224
2189
|
return;
|
|
1225
2190
|
}
|
|
1226
2191
|
if (normalizedTemplate === 'nextjs') {
|
|
1227
|
-
|
|
2192
|
+
applyNextDemoStarter(projectRoot, projectName, pm, flags);
|
|
1228
2193
|
return;
|
|
1229
2194
|
}
|
|
1230
2195
|
if (normalizedTemplate === 'vuejs') {
|
|
1231
|
-
|
|
1232
|
-
...flags,
|
|
1233
|
-
entry: flags.entry ?? 'src/main.ts',
|
|
1234
|
-
});
|
|
1235
|
-
applyVueRsxTemplate(projectRoot, dryRun);
|
|
2196
|
+
applyVueDemoStarter(projectRoot, projectName, pm, flags);
|
|
1236
2197
|
}
|
|
1237
2198
|
});
|
|
1238
2199
|
|
|
@@ -1507,12 +2468,12 @@ function ensureNextGateFile(gateFile, bootstrapFile, dryRun) {
|
|
|
1507
2468
|
const content = useTypeScript
|
|
1508
2469
|
? `'use client';
|
|
1509
2470
|
|
|
1510
|
-
import { type ReactNode, useEffect, useState } from 'react';
|
|
2471
|
+
import { type ReactElement, type ReactNode, useEffect, useState } from 'react';
|
|
1511
2472
|
|
|
1512
2473
|
import { initRsx } from '${importPath}';
|
|
1513
2474
|
|
|
1514
2475
|
// Generated by rsx init
|
|
1515
|
-
export function RsxBootstrapGate(props: { children: ReactNode }):
|
|
2476
|
+
export function RsxBootstrapGate(props: { children: ReactNode }): ReactElement | null {
|
|
1516
2477
|
const [ready, setReady] = useState(false);
|
|
1517
2478
|
|
|
1518
2479
|
useEffect(() => {
|
|
@@ -1733,8 +2694,8 @@ function runInit(flags) {
|
|
|
1733
2694
|
const projectRoot = process.cwd();
|
|
1734
2695
|
|
|
1735
2696
|
if (!skipInstall) {
|
|
1736
|
-
installRuntimePackages(pm, dryRun, tag);
|
|
1737
|
-
installCompilerPackages(pm, dryRun, tag);
|
|
2697
|
+
installRuntimePackages(pm, dryRun, tag, projectRoot, flags);
|
|
2698
|
+
installCompilerPackages(pm, dryRun, tag, projectRoot, flags);
|
|
1738
2699
|
} else {
|
|
1739
2700
|
logInfo('Skipping package installation (--skip-install).');
|
|
1740
2701
|
}
|
|
@@ -1788,11 +2749,117 @@ function runInit(flags) {
|
|
|
1788
2749
|
}
|
|
1789
2750
|
}
|
|
1790
2751
|
|
|
1791
|
-
|
|
1792
|
-
|
|
2752
|
+
logOk('RS-X init completed.');
|
|
2753
|
+
}
|
|
2754
|
+
|
|
2755
|
+
function upsertRsxBuildConfigInPackageJson(projectRoot, dryRun) {
|
|
2756
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
2757
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
2758
|
+
return false;
|
|
1793
2759
|
}
|
|
1794
2760
|
|
|
1795
|
-
|
|
2761
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
2762
|
+
const currentRsx = packageJson.rsx ?? {};
|
|
2763
|
+
const currentBuild = currentRsx.build ?? {};
|
|
2764
|
+
const nextBuild = {
|
|
2765
|
+
preparse: true,
|
|
2766
|
+
preparseFile: 'src/rsx-generated/rsx-aot-preparsed.generated.ts',
|
|
2767
|
+
compiled: true,
|
|
2768
|
+
compiledFile: 'src/rsx-generated/rsx-aot-compiled.generated.ts',
|
|
2769
|
+
registrationFile: 'src/rsx-generated/rsx-aot-registration.generated.ts',
|
|
2770
|
+
compiledResolvedEvaluator: false,
|
|
2771
|
+
...currentBuild,
|
|
2772
|
+
};
|
|
2773
|
+
|
|
2774
|
+
const nextPackageJson = {
|
|
2775
|
+
...packageJson,
|
|
2776
|
+
rsx: {
|
|
2777
|
+
...currentRsx,
|
|
2778
|
+
build: nextBuild,
|
|
2779
|
+
},
|
|
2780
|
+
};
|
|
2781
|
+
|
|
2782
|
+
if (dryRun) {
|
|
2783
|
+
logInfo(`[dry-run] patch ${packageJsonPath} (rsx.build)`);
|
|
2784
|
+
return true;
|
|
2785
|
+
}
|
|
2786
|
+
|
|
2787
|
+
fs.writeFileSync(
|
|
2788
|
+
packageJsonPath,
|
|
2789
|
+
`${JSON.stringify(nextPackageJson, null, 2)}\n`,
|
|
2790
|
+
'utf8',
|
|
2791
|
+
);
|
|
2792
|
+
logOk(`Patched ${packageJsonPath} (rsx.build)`);
|
|
2793
|
+
return true;
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
function ensureAngularProvidersInEntry(entryFile, dryRun) {
|
|
2797
|
+
if (!fs.existsSync(entryFile)) {
|
|
2798
|
+
return false;
|
|
2799
|
+
}
|
|
2800
|
+
|
|
2801
|
+
const original = fs.readFileSync(entryFile, 'utf8');
|
|
2802
|
+
if (original.includes('providexRsx')) {
|
|
2803
|
+
logInfo(`Angular entry already includes providexRsx: ${entryFile}`);
|
|
2804
|
+
return true;
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
if (!original.includes('bootstrapApplication(')) {
|
|
2808
|
+
logWarn(
|
|
2809
|
+
`Could not automatically patch Angular providers in ${entryFile}. Expected bootstrapApplication(...).`,
|
|
2810
|
+
);
|
|
2811
|
+
logInfo(
|
|
2812
|
+
"Manual setup: import { providexRsx } from '@rs-x/angular' and add providers: [...providexRsx()] to bootstrapApplication(...).",
|
|
2813
|
+
);
|
|
2814
|
+
return false;
|
|
2815
|
+
}
|
|
2816
|
+
|
|
2817
|
+
const sourceWithImport = injectImport(
|
|
2818
|
+
original,
|
|
2819
|
+
"import { providexRsx } from '@rs-x/angular';",
|
|
2820
|
+
);
|
|
2821
|
+
|
|
2822
|
+
let updated = sourceWithImport;
|
|
2823
|
+
if (/bootstrapApplication\([\s\S]*?,\s*appConfig\s*\)/mu.test(updated)) {
|
|
2824
|
+
updated = updated.replace(
|
|
2825
|
+
/bootstrapApplication\(([\s\S]*?),\s*appConfig\s*\)/mu,
|
|
2826
|
+
'bootstrapApplication($1, {\n ...appConfig,\n providers: [...(appConfig.providers ?? []), ...providexRsx()],\n})',
|
|
2827
|
+
);
|
|
2828
|
+
} else if (
|
|
2829
|
+
/bootstrapApplication\([\s\S]*?,\s*\{[\s\S]*?providers\s*:/mu.test(updated)
|
|
2830
|
+
) {
|
|
2831
|
+
updated = updated.replace(
|
|
2832
|
+
/providers\s*:\s*\[/mu,
|
|
2833
|
+
'providers: [...providexRsx(), ',
|
|
2834
|
+
);
|
|
2835
|
+
} else if (/bootstrapApplication\([\s\S]*?,\s*\{/mu.test(updated)) {
|
|
2836
|
+
updated = updated.replace(
|
|
2837
|
+
/bootstrapApplication\(([\s\S]*?),\s*\{/mu,
|
|
2838
|
+
'bootstrapApplication($1, {\n providers: [...providexRsx()],',
|
|
2839
|
+
);
|
|
2840
|
+
} else {
|
|
2841
|
+
updated = updated.replace(
|
|
2842
|
+
/bootstrapApplication\(([\s\S]*?)\)\s*(?:\.catch\([\s\S]*?\))?\s*;/mu,
|
|
2843
|
+
'bootstrapApplication($1, {\n providers: [...providexRsx()],\n}).catch((error) => {\n console.error(error);\n});',
|
|
2844
|
+
);
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
if (updated === sourceWithImport) {
|
|
2848
|
+
logWarn(`Could not automatically inject providexRsx into ${entryFile}.`);
|
|
2849
|
+
logInfo(
|
|
2850
|
+
"Manual setup: import { providexRsx } from '@rs-x/angular' and add providers: [...providexRsx()] to bootstrapApplication(...).",
|
|
2851
|
+
);
|
|
2852
|
+
return false;
|
|
2853
|
+
}
|
|
2854
|
+
|
|
2855
|
+
if (dryRun) {
|
|
2856
|
+
logInfo(`[dry-run] patch ${entryFile} (providexRsx)`);
|
|
2857
|
+
return true;
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
fs.writeFileSync(entryFile, updated, 'utf8');
|
|
2861
|
+
logOk(`Patched ${entryFile} to include providexRsx.`);
|
|
2862
|
+
return true;
|
|
1796
2863
|
}
|
|
1797
2864
|
|
|
1798
2865
|
function upsertScriptInPackageJson(
|
|
@@ -1906,109 +2973,6 @@ module.exports = function rsxWebpackLoader(source) {
|
|
|
1906
2973
|
}
|
|
1907
2974
|
|
|
1908
2975
|
function wireRsxVitePlugin(projectRoot, dryRun) {
|
|
1909
|
-
const pluginFile = path.join(projectRoot, 'rsx-vite-plugin.mjs');
|
|
1910
|
-
const pluginSource = `import path from 'node:path';
|
|
1911
|
-
|
|
1912
|
-
import ts from 'typescript';
|
|
1913
|
-
|
|
1914
|
-
import { createExpressionCachePreloadTransformer } from '@rs-x/compiler';
|
|
1915
|
-
|
|
1916
|
-
function normalizeFileName(fileName) {
|
|
1917
|
-
return path.resolve(fileName).replace(/\\\\/gu, '/');
|
|
1918
|
-
}
|
|
1919
|
-
|
|
1920
|
-
function buildTransformedSourceMap(tsconfigPath) {
|
|
1921
|
-
const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
|
|
1922
|
-
if (configFile.error) {
|
|
1923
|
-
return new Map();
|
|
1924
|
-
}
|
|
1925
|
-
|
|
1926
|
-
const parsed = ts.parseJsonConfigFileContent(
|
|
1927
|
-
configFile.config,
|
|
1928
|
-
ts.sys,
|
|
1929
|
-
path.dirname(tsconfigPath),
|
|
1930
|
-
undefined,
|
|
1931
|
-
tsconfigPath,
|
|
1932
|
-
);
|
|
1933
|
-
if (parsed.errors.length > 0) {
|
|
1934
|
-
return new Map();
|
|
1935
|
-
}
|
|
1936
|
-
|
|
1937
|
-
const program = ts.createProgram({
|
|
1938
|
-
rootNames: parsed.fileNames,
|
|
1939
|
-
options: parsed.options,
|
|
1940
|
-
});
|
|
1941
|
-
const transformer = createExpressionCachePreloadTransformer(program);
|
|
1942
|
-
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
1943
|
-
const transformedByFile = new Map();
|
|
1944
|
-
|
|
1945
|
-
for (const sourceFile of program.getSourceFiles()) {
|
|
1946
|
-
if (sourceFile.isDeclarationFile) {
|
|
1947
|
-
continue;
|
|
1948
|
-
}
|
|
1949
|
-
|
|
1950
|
-
if (sourceFile.fileName.includes('/node_modules/')) {
|
|
1951
|
-
continue;
|
|
1952
|
-
}
|
|
1953
|
-
|
|
1954
|
-
const transformed = ts.transform(sourceFile, [transformer]);
|
|
1955
|
-
const transformedSource = transformed.transformed[0];
|
|
1956
|
-
const transformedText = printer.printFile(transformedSource);
|
|
1957
|
-
transformed.dispose();
|
|
1958
|
-
|
|
1959
|
-
transformedByFile.set(normalizeFileName(sourceFile.fileName), transformedText);
|
|
1960
|
-
}
|
|
1961
|
-
|
|
1962
|
-
return transformedByFile;
|
|
1963
|
-
}
|
|
1964
|
-
|
|
1965
|
-
export function rsxVitePlugin(tsconfigPath = 'tsconfig.json') {
|
|
1966
|
-
let transformedByFile = new Map();
|
|
1967
|
-
let resolvedTsConfigPath = '';
|
|
1968
|
-
|
|
1969
|
-
const refresh = () => {
|
|
1970
|
-
transformedByFile = buildTransformedSourceMap(resolvedTsConfigPath);
|
|
1971
|
-
};
|
|
1972
|
-
|
|
1973
|
-
return {
|
|
1974
|
-
name: 'rsx-vite-transform',
|
|
1975
|
-
enforce: 'pre',
|
|
1976
|
-
configResolved(config) {
|
|
1977
|
-
resolvedTsConfigPath = normalizeFileName(path.resolve(config.root, tsconfigPath));
|
|
1978
|
-
refresh();
|
|
1979
|
-
},
|
|
1980
|
-
buildStart() {
|
|
1981
|
-
if (!resolvedTsConfigPath) {
|
|
1982
|
-
resolvedTsConfigPath = normalizeFileName(path.resolve(process.cwd(), tsconfigPath));
|
|
1983
|
-
}
|
|
1984
|
-
refresh();
|
|
1985
|
-
},
|
|
1986
|
-
handleHotUpdate() {
|
|
1987
|
-
refresh();
|
|
1988
|
-
},
|
|
1989
|
-
transform(_code, id) {
|
|
1990
|
-
const normalizedId = normalizeFileName(id.split('?')[0]);
|
|
1991
|
-
const transformed = transformedByFile.get(normalizedId);
|
|
1992
|
-
if (!transformed) {
|
|
1993
|
-
return null;
|
|
1994
|
-
}
|
|
1995
|
-
|
|
1996
|
-
return {
|
|
1997
|
-
code: transformed,
|
|
1998
|
-
map: null,
|
|
1999
|
-
};
|
|
2000
|
-
},
|
|
2001
|
-
};
|
|
2002
|
-
}
|
|
2003
|
-
`;
|
|
2004
|
-
|
|
2005
|
-
if (dryRun) {
|
|
2006
|
-
logInfo(`[dry-run] create ${pluginFile}`);
|
|
2007
|
-
} else {
|
|
2008
|
-
fs.writeFileSync(pluginFile, pluginSource, 'utf8');
|
|
2009
|
-
logOk(`Created ${pluginFile}`);
|
|
2010
|
-
}
|
|
2011
|
-
|
|
2012
2976
|
const viteConfigCandidates = [
|
|
2013
2977
|
'vite.config.ts',
|
|
2014
2978
|
'vite.config.mts',
|
|
@@ -2018,61 +2982,47 @@ export function rsxVitePlugin(tsconfigPath = 'tsconfig.json') {
|
|
|
2018
2982
|
const viteConfigPath = viteConfigCandidates.find((candidate) =>
|
|
2019
2983
|
fs.existsSync(candidate),
|
|
2020
2984
|
);
|
|
2985
|
+
const stalePluginFiles = [
|
|
2986
|
+
path.join(projectRoot, 'rsx-vite-plugin.ts'),
|
|
2987
|
+
path.join(projectRoot, 'rsx-vite-plugin.mjs'),
|
|
2988
|
+
path.join(projectRoot, 'rsx-vite-plugin.d.ts'),
|
|
2989
|
+
];
|
|
2990
|
+
|
|
2021
2991
|
if (!viteConfigPath) {
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
logInfo(
|
|
2026
|
-
"Add it manually: import { rsxVitePlugin } from './rsx-vite-plugin.mjs' and include rsxVitePlugin() in plugins.",
|
|
2027
|
-
);
|
|
2992
|
+
for (const staleFile of stalePluginFiles) {
|
|
2993
|
+
removeFileOrDirectoryWithDryRun(staleFile, dryRun);
|
|
2994
|
+
}
|
|
2028
2995
|
return;
|
|
2029
2996
|
}
|
|
2030
2997
|
|
|
2031
2998
|
const original = fs.readFileSync(viteConfigPath, 'utf8');
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2999
|
+
const updated = original
|
|
3000
|
+
.replace(
|
|
3001
|
+
/import\s+\{\s*rsxVitePlugin\s*\}\s+from\s+['"]\.\/rsx-vite-plugin(?:\.mjs)?['"];\n?/gu,
|
|
3002
|
+
'',
|
|
3003
|
+
)
|
|
3004
|
+
.replace(/rsxVitePlugin\(\)\s*,\s*/gu, '')
|
|
3005
|
+
.replace(/,\s*rsxVitePlugin\(\)/gu, '')
|
|
3006
|
+
.replace(/\[\s*rsxVitePlugin\(\)\s*\]/gu, '[]');
|
|
3007
|
+
|
|
3008
|
+
if (updated !== original) {
|
|
3009
|
+
if (dryRun) {
|
|
3010
|
+
logInfo(
|
|
3011
|
+
`[dry-run] patch ${viteConfigPath} (remove legacy RS-X Vite plugin)`,
|
|
3012
|
+
);
|
|
3013
|
+
} else {
|
|
3014
|
+
fs.writeFileSync(viteConfigPath, updated, 'utf8');
|
|
3015
|
+
logOk(`Patched ${viteConfigPath} (removed legacy RS-X Vite plugin).`);
|
|
2048
3016
|
}
|
|
2049
|
-
lines.splice(insertAt, 0, importStatement);
|
|
2050
|
-
updated = lines.join('\n');
|
|
2051
|
-
}
|
|
2052
|
-
|
|
2053
|
-
if (/plugins\s*:\s*\[/u.test(updated)) {
|
|
2054
|
-
updated = updated.replace(
|
|
2055
|
-
/plugins\s*:\s*\[/u,
|
|
2056
|
-
'plugins: [rsxVitePlugin(), ',
|
|
2057
|
-
);
|
|
2058
|
-
} else if (/defineConfig\s*\(\s*\{/u.test(updated)) {
|
|
2059
|
-
updated = updated.replace(
|
|
2060
|
-
/defineConfig\s*\(\s*\{/u,
|
|
2061
|
-
'defineConfig({\n plugins: [rsxVitePlugin()],',
|
|
2062
|
-
);
|
|
2063
3017
|
} else {
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
3018
|
+
logInfo(
|
|
3019
|
+
`Vite config already uses the default plugin list: ${viteConfigPath}`,
|
|
3020
|
+
);
|
|
2067
3021
|
}
|
|
2068
3022
|
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
return;
|
|
3023
|
+
for (const staleFile of stalePluginFiles) {
|
|
3024
|
+
removeFileOrDirectoryWithDryRun(staleFile, dryRun);
|
|
2072
3025
|
}
|
|
2073
|
-
|
|
2074
|
-
fs.writeFileSync(viteConfigPath, updated, 'utf8');
|
|
2075
|
-
logOk(`Patched ${viteConfigPath} with RS-X Vite plugin.`);
|
|
2076
3026
|
}
|
|
2077
3027
|
|
|
2078
3028
|
function wireRsxNextWebpack(projectRoot, dryRun) {
|
|
@@ -2154,91 +3104,6 @@ ${patchBlock}
|
|
|
2154
3104
|
logOk(`Patched ${nextConfigJs} with RS-X webpack loader.`);
|
|
2155
3105
|
}
|
|
2156
3106
|
|
|
2157
|
-
function wireRsxAngularWebpack(projectRoot, dryRun) {
|
|
2158
|
-
const angularJsonPath = path.join(projectRoot, 'angular.json');
|
|
2159
|
-
if (!fs.existsSync(angularJsonPath)) {
|
|
2160
|
-
logWarn('angular.json not found. Skipping Angular build integration.');
|
|
2161
|
-
return;
|
|
2162
|
-
}
|
|
2163
|
-
|
|
2164
|
-
createRsxWebpackLoaderFile(projectRoot, dryRun);
|
|
2165
|
-
|
|
2166
|
-
const webpackConfigPath = path.join(projectRoot, 'rsx-angular-webpack.cjs');
|
|
2167
|
-
const webpackConfigSource = `const path = require('node:path');
|
|
2168
|
-
|
|
2169
|
-
module.exports = {
|
|
2170
|
-
module: {
|
|
2171
|
-
rules: [
|
|
2172
|
-
{
|
|
2173
|
-
test: /\\.[jt]sx?$/u,
|
|
2174
|
-
exclude: /node_modules/u,
|
|
2175
|
-
use: [
|
|
2176
|
-
{
|
|
2177
|
-
loader: path.resolve(__dirname, './rsx-webpack-loader.cjs'),
|
|
2178
|
-
},
|
|
2179
|
-
],
|
|
2180
|
-
},
|
|
2181
|
-
],
|
|
2182
|
-
},
|
|
2183
|
-
};
|
|
2184
|
-
`;
|
|
2185
|
-
|
|
2186
|
-
if (dryRun) {
|
|
2187
|
-
logInfo(`[dry-run] create ${webpackConfigPath}`);
|
|
2188
|
-
} else {
|
|
2189
|
-
fs.writeFileSync(webpackConfigPath, webpackConfigSource, 'utf8');
|
|
2190
|
-
logOk(`Created ${webpackConfigPath}`);
|
|
2191
|
-
}
|
|
2192
|
-
|
|
2193
|
-
const angularJson = JSON.parse(fs.readFileSync(angularJsonPath, 'utf8'));
|
|
2194
|
-
const projects = angularJson.projects ?? {};
|
|
2195
|
-
const projectNames = Object.keys(projects);
|
|
2196
|
-
if (projectNames.length === 0) {
|
|
2197
|
-
logWarn('No Angular projects found in angular.json.');
|
|
2198
|
-
return;
|
|
2199
|
-
}
|
|
2200
|
-
|
|
2201
|
-
const patchPath = 'rsx-angular-webpack.cjs';
|
|
2202
|
-
for (const projectName of projectNames) {
|
|
2203
|
-
const project = projects[projectName];
|
|
2204
|
-
const architect = project.architect ?? project.targets;
|
|
2205
|
-
if (!architect?.build) {
|
|
2206
|
-
continue;
|
|
2207
|
-
}
|
|
2208
|
-
|
|
2209
|
-
const build = architect.build;
|
|
2210
|
-
if (build.builder !== '@angular-builders/custom-webpack:browser') {
|
|
2211
|
-
build.builder = '@angular-builders/custom-webpack:browser';
|
|
2212
|
-
}
|
|
2213
|
-
build.options = build.options ?? {};
|
|
2214
|
-
build.options.customWebpackConfig = build.options.customWebpackConfig ?? {};
|
|
2215
|
-
build.options.customWebpackConfig.path = patchPath;
|
|
2216
|
-
|
|
2217
|
-
if (architect.serve) {
|
|
2218
|
-
const serve = architect.serve;
|
|
2219
|
-
if (serve.builder !== '@angular-builders/custom-webpack:dev-server') {
|
|
2220
|
-
serve.builder = '@angular-builders/custom-webpack:dev-server';
|
|
2221
|
-
}
|
|
2222
|
-
serve.options = serve.options ?? {};
|
|
2223
|
-
serve.options.buildTarget =
|
|
2224
|
-
serve.options.buildTarget ?? `${projectName}:build`;
|
|
2225
|
-
serve.options.browserTarget =
|
|
2226
|
-
serve.options.browserTarget ?? `${projectName}:build`;
|
|
2227
|
-
}
|
|
2228
|
-
}
|
|
2229
|
-
|
|
2230
|
-
if (dryRun) {
|
|
2231
|
-
logInfo(`[dry-run] patch ${angularJsonPath}`);
|
|
2232
|
-
} else {
|
|
2233
|
-
fs.writeFileSync(
|
|
2234
|
-
angularJsonPath,
|
|
2235
|
-
`${JSON.stringify(angularJson, null, 2)}\n`,
|
|
2236
|
-
'utf8',
|
|
2237
|
-
);
|
|
2238
|
-
logOk(`Patched ${angularJsonPath} for RS-X Angular webpack integration.`);
|
|
2239
|
-
}
|
|
2240
|
-
}
|
|
2241
|
-
|
|
2242
3107
|
function runSetupReact(flags) {
|
|
2243
3108
|
const dryRun = Boolean(flags['dry-run']);
|
|
2244
3109
|
const pm = detectPackageManager(flags.pm);
|
|
@@ -2266,19 +3131,48 @@ function runSetupReact(flags) {
|
|
|
2266
3131
|
'skip-vscode': true,
|
|
2267
3132
|
});
|
|
2268
3133
|
if (!Boolean(flags['skip-install'])) {
|
|
2269
|
-
|
|
3134
|
+
const specs = resolveLocalRsxSpecs(projectRoot, flags, {
|
|
3135
|
+
tag,
|
|
3136
|
+
includeReactPackage: true,
|
|
3137
|
+
});
|
|
3138
|
+
installResolvedPackages(pm, ['@rs-x/react'], {
|
|
2270
3139
|
dev: false,
|
|
2271
3140
|
dryRun,
|
|
2272
3141
|
tag,
|
|
3142
|
+
specs,
|
|
3143
|
+
cwd: projectRoot,
|
|
2273
3144
|
label: 'RS-X React bindings',
|
|
2274
3145
|
});
|
|
3146
|
+
installResolvedPackages(pm, ['@rs-x/cli'], {
|
|
3147
|
+
dev: true,
|
|
3148
|
+
dryRun,
|
|
3149
|
+
tag,
|
|
3150
|
+
specs,
|
|
3151
|
+
cwd: projectRoot,
|
|
3152
|
+
label: 'RS-X CLI',
|
|
3153
|
+
});
|
|
2275
3154
|
} else {
|
|
2276
3155
|
logInfo('Skipping RS-X React bindings install (--skip-install).');
|
|
2277
3156
|
}
|
|
3157
|
+
upsertScriptInPackageJson(
|
|
3158
|
+
projectRoot,
|
|
3159
|
+
'build:rsx',
|
|
3160
|
+
'rsx build --project tsconfig.json --no-emit --prod',
|
|
3161
|
+
dryRun,
|
|
3162
|
+
);
|
|
3163
|
+
upsertScriptInPackageJson(
|
|
3164
|
+
projectRoot,
|
|
3165
|
+
'dev',
|
|
3166
|
+
'npm run build:rsx && vite',
|
|
3167
|
+
dryRun,
|
|
3168
|
+
);
|
|
3169
|
+
upsertScriptInPackageJson(
|
|
3170
|
+
projectRoot,
|
|
3171
|
+
'build',
|
|
3172
|
+
'npm run build:rsx && vite build',
|
|
3173
|
+
dryRun,
|
|
3174
|
+
);
|
|
2278
3175
|
wireRsxVitePlugin(projectRoot, dryRun);
|
|
2279
|
-
if (!Boolean(flags['skip-vscode'])) {
|
|
2280
|
-
installVsCodeExtension(flags);
|
|
2281
|
-
}
|
|
2282
3176
|
logOk('RS-X React setup completed.');
|
|
2283
3177
|
}
|
|
2284
3178
|
|
|
@@ -2286,24 +3180,28 @@ function runSetupNext(flags) {
|
|
|
2286
3180
|
const dryRun = Boolean(flags['dry-run']);
|
|
2287
3181
|
const pm = detectPackageManager(flags.pm);
|
|
2288
3182
|
const tag = resolveInstallTag(flags);
|
|
3183
|
+
const projectRoot = process.cwd();
|
|
2289
3184
|
runInit({
|
|
2290
3185
|
...flags,
|
|
2291
3186
|
'skip-vscode': true,
|
|
2292
3187
|
});
|
|
2293
3188
|
if (!Boolean(flags['skip-install'])) {
|
|
2294
|
-
|
|
3189
|
+
const specs = resolveLocalRsxSpecs(projectRoot, flags, {
|
|
3190
|
+
tag,
|
|
3191
|
+
includeReactPackage: true,
|
|
3192
|
+
});
|
|
3193
|
+
installResolvedPackages(pm, ['@rs-x/react'], {
|
|
2295
3194
|
dev: false,
|
|
2296
3195
|
dryRun,
|
|
2297
3196
|
tag,
|
|
3197
|
+
specs,
|
|
3198
|
+
cwd: projectRoot,
|
|
2298
3199
|
label: 'RS-X React bindings',
|
|
2299
3200
|
});
|
|
2300
3201
|
} else {
|
|
2301
3202
|
logInfo('Skipping RS-X React bindings install (--skip-install).');
|
|
2302
3203
|
}
|
|
2303
|
-
wireRsxNextWebpack(
|
|
2304
|
-
if (!Boolean(flags['skip-vscode'])) {
|
|
2305
|
-
installVsCodeExtension(flags);
|
|
2306
|
-
}
|
|
3204
|
+
wireRsxNextWebpack(projectRoot, dryRun);
|
|
2307
3205
|
logOk('RS-X Next.js setup completed.');
|
|
2308
3206
|
}
|
|
2309
3207
|
|
|
@@ -2311,24 +3209,64 @@ function runSetupVue(flags) {
|
|
|
2311
3209
|
const dryRun = Boolean(flags['dry-run']);
|
|
2312
3210
|
const pm = detectPackageManager(flags.pm);
|
|
2313
3211
|
const tag = resolveInstallTag(flags);
|
|
3212
|
+
const projectRoot = process.cwd();
|
|
2314
3213
|
runInit({
|
|
2315
3214
|
...flags,
|
|
2316
3215
|
'skip-vscode': true,
|
|
2317
3216
|
});
|
|
2318
3217
|
if (!Boolean(flags['skip-install'])) {
|
|
2319
|
-
|
|
3218
|
+
const specs = resolveLocalRsxSpecs(projectRoot, flags, {
|
|
3219
|
+
tag,
|
|
3220
|
+
includeVuePackage: true,
|
|
3221
|
+
});
|
|
3222
|
+
installResolvedPackages(pm, ['@rs-x/vue'], {
|
|
2320
3223
|
dev: false,
|
|
2321
3224
|
dryRun,
|
|
2322
3225
|
tag,
|
|
3226
|
+
specs,
|
|
3227
|
+
cwd: projectRoot,
|
|
2323
3228
|
label: 'RS-X Vue bindings',
|
|
2324
3229
|
});
|
|
3230
|
+
installResolvedPackages(pm, ['@rs-x/cli'], {
|
|
3231
|
+
dev: true,
|
|
3232
|
+
dryRun,
|
|
3233
|
+
tag,
|
|
3234
|
+
specs,
|
|
3235
|
+
cwd: projectRoot,
|
|
3236
|
+
label: 'RS-X CLI',
|
|
3237
|
+
});
|
|
2325
3238
|
} else {
|
|
2326
3239
|
logInfo('Skipping RS-X Vue bindings install (--skip-install).');
|
|
2327
3240
|
}
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
3241
|
+
upsertScriptInPackageJson(
|
|
3242
|
+
projectRoot,
|
|
3243
|
+
'build:rsx',
|
|
3244
|
+
'rsx build --project tsconfig.app.json --no-emit --prod',
|
|
3245
|
+
dryRun,
|
|
3246
|
+
);
|
|
3247
|
+
upsertScriptInPackageJson(
|
|
3248
|
+
projectRoot,
|
|
3249
|
+
'typecheck:rsx',
|
|
3250
|
+
'rsx typecheck --project tsconfig.app.json',
|
|
3251
|
+
dryRun,
|
|
3252
|
+
);
|
|
3253
|
+
upsertScriptInPackageJson(
|
|
3254
|
+
projectRoot,
|
|
3255
|
+
'dev',
|
|
3256
|
+
'npm run build:rsx && vite',
|
|
3257
|
+
dryRun,
|
|
3258
|
+
);
|
|
3259
|
+
upsertScriptInPackageJson(
|
|
3260
|
+
projectRoot,
|
|
3261
|
+
'build',
|
|
3262
|
+
'npm run build:rsx && vue-tsc -b && vite build',
|
|
3263
|
+
dryRun,
|
|
3264
|
+
);
|
|
3265
|
+
const vueTsConfigPath = path.join(projectRoot, 'tsconfig.app.json');
|
|
3266
|
+
upsertTypescriptPluginInTsConfig(vueTsConfigPath, dryRun);
|
|
3267
|
+
ensureTsConfigIncludePattern(vueTsConfigPath, 'src/**/*.d.ts', dryRun);
|
|
3268
|
+
ensureVueEnvTypes(projectRoot, dryRun);
|
|
3269
|
+
wireRsxVitePlugin(projectRoot, dryRun);
|
|
2332
3270
|
logOk('RS-X Vue setup completed.');
|
|
2333
3271
|
}
|
|
2334
3272
|
|
|
@@ -2336,47 +3274,118 @@ function runSetupAngular(flags) {
|
|
|
2336
3274
|
const dryRun = Boolean(flags['dry-run']);
|
|
2337
3275
|
const pm = detectPackageManager(flags.pm);
|
|
2338
3276
|
const tag = resolveInstallTag(flags);
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
3277
|
+
const projectRoot = process.cwd();
|
|
3278
|
+
const angularTsConfigPath = resolveAngularProjectTsConfig(projectRoot);
|
|
3279
|
+
const angularTsConfigRelative = path
|
|
3280
|
+
.relative(projectRoot, angularTsConfigPath)
|
|
3281
|
+
.replace(/\\/gu, '/');
|
|
2344
3282
|
|
|
2345
3283
|
if (!Boolean(flags['skip-install'])) {
|
|
2346
|
-
|
|
3284
|
+
installRuntimePackages(pm, dryRun, tag, projectRoot, flags);
|
|
3285
|
+
installCompilerPackages(pm, dryRun, tag, projectRoot, flags);
|
|
3286
|
+
const specs = resolveLocalRsxSpecs(projectRoot, flags, {
|
|
3287
|
+
tag,
|
|
3288
|
+
includeAngularPackage: true,
|
|
3289
|
+
});
|
|
3290
|
+
installResolvedPackages(pm, ['@rs-x/angular'], {
|
|
2347
3291
|
dev: false,
|
|
2348
3292
|
dryRun,
|
|
2349
3293
|
tag,
|
|
3294
|
+
specs,
|
|
3295
|
+
cwd: projectRoot,
|
|
2350
3296
|
label: 'RS-X Angular bindings',
|
|
2351
3297
|
});
|
|
2352
|
-
|
|
3298
|
+
installResolvedPackages(pm, ['@rs-x/cli'], {
|
|
2353
3299
|
dev: true,
|
|
2354
3300
|
dryRun,
|
|
2355
|
-
|
|
3301
|
+
tag,
|
|
3302
|
+
specs,
|
|
3303
|
+
cwd: projectRoot,
|
|
3304
|
+
label: 'RS-X CLI',
|
|
2356
3305
|
});
|
|
2357
3306
|
} else {
|
|
3307
|
+
logInfo('Skipping package installation (--skip-install).');
|
|
3308
|
+
}
|
|
3309
|
+
|
|
3310
|
+
const entryFile = resolveEntryFile(projectRoot, 'angular', flags.entry);
|
|
3311
|
+
if (entryFile) {
|
|
3312
|
+
logInfo(`Using Angular entry file: ${entryFile}`);
|
|
3313
|
+
ensureAngularProvidersInEntry(entryFile, dryRun);
|
|
3314
|
+
} else {
|
|
3315
|
+
logWarn('Could not detect an Angular entry file automatically.');
|
|
2358
3316
|
logInfo(
|
|
2359
|
-
'
|
|
3317
|
+
'Manual setup: add providexRsx() to bootstrapApplication(...) in your main entry file.',
|
|
2360
3318
|
);
|
|
2361
3319
|
}
|
|
2362
3320
|
|
|
2363
|
-
|
|
3321
|
+
upsertRsxBuildConfigInPackageJson(projectRoot, dryRun);
|
|
3322
|
+
|
|
2364
3323
|
upsertScriptInPackageJson(
|
|
2365
|
-
|
|
3324
|
+
projectRoot,
|
|
2366
3325
|
'build:rsx',
|
|
2367
|
-
|
|
3326
|
+
`rsx build --project ${angularTsConfigRelative} --no-emit --prod`,
|
|
2368
3327
|
dryRun,
|
|
2369
3328
|
);
|
|
2370
3329
|
upsertScriptInPackageJson(
|
|
2371
|
-
|
|
3330
|
+
projectRoot,
|
|
2372
3331
|
'typecheck:rsx',
|
|
2373
|
-
|
|
3332
|
+
`rsx typecheck --project ${angularTsConfigRelative}`,
|
|
3333
|
+
dryRun,
|
|
3334
|
+
);
|
|
3335
|
+
upsertScriptInPackageJson(
|
|
3336
|
+
projectRoot,
|
|
3337
|
+
'prebuild',
|
|
3338
|
+
'npm run build:rsx',
|
|
3339
|
+
dryRun,
|
|
3340
|
+
);
|
|
3341
|
+
upsertScriptInPackageJson(
|
|
3342
|
+
projectRoot,
|
|
3343
|
+
'start',
|
|
3344
|
+
'npm run build:rsx && ng serve',
|
|
2374
3345
|
dryRun,
|
|
2375
3346
|
);
|
|
2376
3347
|
|
|
2377
|
-
|
|
2378
|
-
|
|
3348
|
+
const rsxRegistrationFile = path.join(
|
|
3349
|
+
projectRoot,
|
|
3350
|
+
'src/rsx-generated/rsx-aot-registration.generated.ts',
|
|
3351
|
+
);
|
|
3352
|
+
const angularJsonPath = path.join(projectRoot, 'angular.json');
|
|
3353
|
+
if (fs.existsSync(angularJsonPath)) {
|
|
3354
|
+
const angularJson = JSON.parse(fs.readFileSync(angularJsonPath, 'utf8'));
|
|
3355
|
+
const projects = angularJson.projects ?? {};
|
|
3356
|
+
for (const projectConfig of Object.values(projects)) {
|
|
3357
|
+
const buildOptions = projectConfig?.architect?.build?.options;
|
|
3358
|
+
if (buildOptions && typeof buildOptions === 'object') {
|
|
3359
|
+
buildOptions.preserveSymlinks = true;
|
|
3360
|
+
}
|
|
3361
|
+
if (
|
|
3362
|
+
projectConfig?.architect?.build?.configurations?.production?.budgets
|
|
3363
|
+
) {
|
|
3364
|
+
delete projectConfig.architect.build.configurations.production.budgets;
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
if (dryRun) {
|
|
3368
|
+
logInfo(
|
|
3369
|
+
`[dry-run] patch ${angularJsonPath} (preserveSymlinks, production budgets)`,
|
|
3370
|
+
);
|
|
3371
|
+
} else {
|
|
3372
|
+
fs.writeFileSync(
|
|
3373
|
+
angularJsonPath,
|
|
3374
|
+
`${JSON.stringify(angularJson, null, 2)}\n`,
|
|
3375
|
+
'utf8',
|
|
3376
|
+
);
|
|
3377
|
+
logOk(
|
|
3378
|
+
`Patched ${angularJsonPath} (preserveSymlinks, production budgets).`,
|
|
3379
|
+
);
|
|
3380
|
+
}
|
|
2379
3381
|
}
|
|
3382
|
+
ensureAngularPolyfillsContainsFile({
|
|
3383
|
+
projectRoot,
|
|
3384
|
+
configPath: angularTsConfigPath,
|
|
3385
|
+
filePath: rsxRegistrationFile,
|
|
3386
|
+
dryRun,
|
|
3387
|
+
});
|
|
3388
|
+
|
|
2380
3389
|
logOk('RS-X Angular setup completed.');
|
|
2381
3390
|
}
|
|
2382
3391
|
|
|
@@ -2411,9 +3420,20 @@ function runSetupAuto(flags) {
|
|
|
2411
3420
|
|
|
2412
3421
|
logInfo('No framework-specific setup detected; running generic setup.');
|
|
2413
3422
|
const pm = detectPackageManager(flags.pm);
|
|
2414
|
-
installRuntimePackages(
|
|
2415
|
-
|
|
2416
|
-
|
|
3423
|
+
installRuntimePackages(
|
|
3424
|
+
pm,
|
|
3425
|
+
Boolean(flags['dry-run']),
|
|
3426
|
+
tag,
|
|
3427
|
+
projectRoot,
|
|
3428
|
+
flags,
|
|
3429
|
+
);
|
|
3430
|
+
installCompilerPackages(
|
|
3431
|
+
pm,
|
|
3432
|
+
Boolean(flags['dry-run']),
|
|
3433
|
+
tag,
|
|
3434
|
+
projectRoot,
|
|
3435
|
+
flags,
|
|
3436
|
+
);
|
|
2417
3437
|
}
|
|
2418
3438
|
|
|
2419
3439
|
function resolveProjectModule(projectRoot, moduleName) {
|
|
@@ -2978,9 +3998,14 @@ function ensureAngularPolyfillsContainsFile({
|
|
|
2978
3998
|
});
|
|
2979
3999
|
|
|
2980
4000
|
const selectedEntries = targetEntries.length > 0 ? targetEntries : entries;
|
|
2981
|
-
const
|
|
4001
|
+
const polyfillsRelativePath = path
|
|
2982
4002
|
.relative(projectRoot, filePath)
|
|
2983
4003
|
.replace(/\\/g, '/');
|
|
4004
|
+
const polyfillsPath =
|
|
4005
|
+
polyfillsRelativePath.startsWith('./') ||
|
|
4006
|
+
polyfillsRelativePath.startsWith('../')
|
|
4007
|
+
? polyfillsRelativePath
|
|
4008
|
+
: `./${polyfillsRelativePath}`;
|
|
2984
4009
|
|
|
2985
4010
|
let changed = false;
|
|
2986
4011
|
const isRsxAotRegistrationEntry = (entry) =>
|
|
@@ -3256,12 +4281,17 @@ function printProjectHelp() {
|
|
|
3256
4281
|
console.log('What it does:');
|
|
3257
4282
|
console.log(' - Creates a new project folder');
|
|
3258
4283
|
console.log(' - Supports templates: angular, vuejs, react, nextjs, nodejs');
|
|
4284
|
+
console.log(
|
|
4285
|
+
' - Angular generates the RS-X virtual-table demo starter on top of the latest Angular scaffold',
|
|
4286
|
+
);
|
|
3259
4287
|
console.log(' - Scaffolds framework app and wires RS-X bootstrap/setup');
|
|
3260
4288
|
console.log(' - Writes package.json with RS-X dependencies');
|
|
3261
4289
|
console.log(
|
|
3262
4290
|
' - Adds tsconfig + TypeScript plugin config for editor support',
|
|
3263
4291
|
);
|
|
3264
|
-
console.log(
|
|
4292
|
+
console.log(
|
|
4293
|
+
' - For Angular template: uses the latest Angular CLI scaffold, then applies the RS-X demo starter',
|
|
4294
|
+
);
|
|
3265
4295
|
console.log(' - For React/Next templates: also installs @rs-x/react');
|
|
3266
4296
|
console.log(' - For Vue template: also installs @rs-x/vue');
|
|
3267
4297
|
console.log(' - Installs dependencies (unless --skip-install)');
|
|
@@ -3338,6 +4368,7 @@ function printTypecheckHelp() {
|
|
|
3338
4368
|
function printVersionHelp() {
|
|
3339
4369
|
console.log('Usage:');
|
|
3340
4370
|
console.log(' rsx version');
|
|
4371
|
+
console.log(' rsx v');
|
|
3341
4372
|
console.log(' rsx -v');
|
|
3342
4373
|
console.log(' rsx -version');
|
|
3343
4374
|
console.log(' rsx --version');
|
|
@@ -3349,6 +4380,7 @@ function isHelpToken(value) {
|
|
|
3349
4380
|
|
|
3350
4381
|
function isVersionToken(value) {
|
|
3351
4382
|
return (
|
|
4383
|
+
value === 'v' ||
|
|
3352
4384
|
value === '-v' ||
|
|
3353
4385
|
value === '--version' ||
|
|
3354
4386
|
value === '-version' ||
|
|
@@ -3409,6 +4441,7 @@ function printHelpFor(command, target) {
|
|
|
3409
4441
|
}
|
|
3410
4442
|
|
|
3411
4443
|
if (
|
|
4444
|
+
command === 'v' ||
|
|
3412
4445
|
command === 'version' ||
|
|
3413
4446
|
command === '-v' ||
|
|
3414
4447
|
command === '--version' ||
|
|
@@ -3495,7 +4528,13 @@ function main() {
|
|
|
3495
4528
|
if (command === 'install' && target === 'compiler') {
|
|
3496
4529
|
const pm = detectPackageManager(flags.pm);
|
|
3497
4530
|
const tag = resolveInstallTag(flags);
|
|
3498
|
-
installCompilerPackages(
|
|
4531
|
+
installCompilerPackages(
|
|
4532
|
+
pm,
|
|
4533
|
+
Boolean(flags['dry-run']),
|
|
4534
|
+
tag,
|
|
4535
|
+
process.cwd(),
|
|
4536
|
+
flags,
|
|
4537
|
+
);
|
|
3499
4538
|
return;
|
|
3500
4539
|
}
|
|
3501
4540
|
|