@rs-x/cli 2.0.0-next.0 → 2.0.0-next.11
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 +10 -4
- package/bin/rsx.cjs +679 -153
- package/package.json +14 -1
- package/{rs-x-vscode-extension-2.0.0-next.0.vsix → rs-x-vscode-extension-2.0.0-next.11.vsix} +0 -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/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 +247 -0
- package/templates/react-demo/src/app/virtual-table/virtual-table-data.service.ts +126 -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 +83 -0
- package/templates/react-demo/src/main.tsx +23 -0
- package/templates/react-demo/src/rsx-bootstrap.ts +18 -0
- package/templates/react-demo/src/styles.css +422 -0
- package/templates/react-demo/tsconfig.json +17 -0
package/bin/rsx.cjs
CHANGED
|
@@ -5,8 +5,28 @@ 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
|
+
);
|
|
10
30
|
const RUNTIME_PACKAGES = [
|
|
11
31
|
'@rs-x/core',
|
|
12
32
|
'@rs-x/state-manager',
|
|
@@ -178,15 +198,38 @@ function detectPackageManager(explicitPm) {
|
|
|
178
198
|
return 'npm';
|
|
179
199
|
}
|
|
180
200
|
|
|
201
|
+
function applyTagToPackages(packages, tag) {
|
|
202
|
+
return packages.map((pkg) => {
|
|
203
|
+
const lastAt = pkg.lastIndexOf('@');
|
|
204
|
+
const slashIndex = pkg.indexOf('/');
|
|
205
|
+
const hasVersion = pkg.startsWith('@') ? lastAt > slashIndex : lastAt > 0;
|
|
206
|
+
if (hasVersion) {
|
|
207
|
+
return pkg;
|
|
208
|
+
}
|
|
209
|
+
return `${pkg}@${tag}`;
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function resolveInstallTag(flags) {
|
|
214
|
+
return parseBooleanFlag(flags.next, false) ? 'next' : undefined;
|
|
215
|
+
}
|
|
216
|
+
|
|
181
217
|
function installPackages(pm, packages, options = {}) {
|
|
182
|
-
const { dev = false, dryRun = false, label = 'packages' } = options;
|
|
218
|
+
const { dev = false, dryRun = false, label = 'packages', tag } = options;
|
|
219
|
+
const resolvedPackages = tag ? applyTagToPackages(packages, tag) : packages;
|
|
183
220
|
const argsByPm = {
|
|
184
|
-
pnpm: dev
|
|
221
|
+
pnpm: dev
|
|
222
|
+
? ['add', '-D', ...resolvedPackages]
|
|
223
|
+
: ['add', ...resolvedPackages],
|
|
185
224
|
npm: dev
|
|
186
|
-
? ['install', '--save-dev', ...
|
|
187
|
-
: ['install', '--save', ...
|
|
188
|
-
yarn: dev
|
|
189
|
-
|
|
225
|
+
? ['install', '--save-dev', ...resolvedPackages]
|
|
226
|
+
: ['install', '--save', ...resolvedPackages],
|
|
227
|
+
yarn: dev
|
|
228
|
+
? ['add', '--dev', ...resolvedPackages]
|
|
229
|
+
: ['add', ...resolvedPackages],
|
|
230
|
+
bun: dev
|
|
231
|
+
? ['add', '--dev', ...resolvedPackages]
|
|
232
|
+
: ['add', ...resolvedPackages],
|
|
190
233
|
};
|
|
191
234
|
|
|
192
235
|
const installArgs = argsByPm[pm];
|
|
@@ -195,23 +238,26 @@ function installPackages(pm, packages, options = {}) {
|
|
|
195
238
|
process.exit(1);
|
|
196
239
|
}
|
|
197
240
|
|
|
198
|
-
|
|
241
|
+
const tagInfo = tag ? ` (tag: ${tag})` : '';
|
|
242
|
+
logInfo(`Installing ${label} with ${pm}${tagInfo}...`);
|
|
199
243
|
run(pm, installArgs, { dryRun });
|
|
200
244
|
logOk(`Installed ${label}.`);
|
|
201
245
|
}
|
|
202
246
|
|
|
203
|
-
function installRuntimePackages(pm, dryRun) {
|
|
247
|
+
function installRuntimePackages(pm, dryRun, tag) {
|
|
204
248
|
installPackages(pm, RUNTIME_PACKAGES, {
|
|
205
249
|
dev: false,
|
|
206
250
|
dryRun,
|
|
251
|
+
tag,
|
|
207
252
|
label: 'runtime RS-X packages',
|
|
208
253
|
});
|
|
209
254
|
}
|
|
210
255
|
|
|
211
|
-
function installCompilerPackages(pm, dryRun) {
|
|
256
|
+
function installCompilerPackages(pm, dryRun, tag) {
|
|
212
257
|
installPackages(pm, COMPILER_PACKAGES, {
|
|
213
258
|
dev: true,
|
|
214
259
|
dryRun,
|
|
260
|
+
tag,
|
|
215
261
|
label: 'compiler tooling',
|
|
216
262
|
});
|
|
217
263
|
}
|
|
@@ -236,14 +282,50 @@ function installVsCodeExtension(flags) {
|
|
|
236
282
|
return;
|
|
237
283
|
}
|
|
238
284
|
|
|
239
|
-
|
|
285
|
+
installBundledVsix(dryRun, force);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function resolveBundledVsix() {
|
|
289
|
+
const packageRoot = path.resolve(__dirname, '..');
|
|
290
|
+
const candidates = fs
|
|
291
|
+
.readdirSync(packageRoot)
|
|
292
|
+
.filter((name) => /^rs-x-vscode-extension-.*\.vsix$/u.test(name))
|
|
293
|
+
.map((name) => path.join(packageRoot, name));
|
|
294
|
+
|
|
295
|
+
if (candidates.length === 0) {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const latest = candidates
|
|
300
|
+
.map((fullPath) => ({
|
|
301
|
+
fullPath,
|
|
302
|
+
mtimeMs: fs.statSync(fullPath).mtimeMs,
|
|
303
|
+
}))
|
|
304
|
+
.sort((a, b) => b.mtimeMs - a.mtimeMs)[0];
|
|
305
|
+
|
|
306
|
+
return latest?.fullPath ?? null;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function installBundledVsix(dryRun, force) {
|
|
310
|
+
const bundledVsix = resolveBundledVsix();
|
|
311
|
+
if (!bundledVsix) {
|
|
312
|
+
logWarn(
|
|
313
|
+
'No bundled VSIX found in @rs-x/cli. Skipping VS Code extension install.',
|
|
314
|
+
);
|
|
315
|
+
logInfo(
|
|
316
|
+
'If you are developing in the rs-x repo, use `rsx install vscode --local` instead.',
|
|
317
|
+
);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const args = ['--install-extension', bundledVsix];
|
|
240
322
|
if (force) {
|
|
241
323
|
args.push('--force');
|
|
242
324
|
}
|
|
243
325
|
|
|
244
|
-
logInfo(`Installing ${
|
|
326
|
+
logInfo(`Installing bundled VSIX from ${bundledVsix}...`);
|
|
245
327
|
run('code', args, { dryRun });
|
|
246
|
-
logOk('VS Code extension installed.');
|
|
328
|
+
logOk('VS Code extension installed from bundled VSIX.');
|
|
247
329
|
}
|
|
248
330
|
|
|
249
331
|
function installLocalVsix(dryRun, force) {
|
|
@@ -559,6 +641,85 @@ function writeFileWithDryRun(filePath, content, dryRun) {
|
|
|
559
641
|
fs.writeFileSync(filePath, content, 'utf8');
|
|
560
642
|
}
|
|
561
643
|
|
|
644
|
+
function copyPathWithDryRun(sourcePath, targetPath, dryRun) {
|
|
645
|
+
if (dryRun) {
|
|
646
|
+
logInfo(`[dry-run] copy ${sourcePath} -> ${targetPath}`);
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
const stat = fs.statSync(sourcePath);
|
|
651
|
+
if (stat.isDirectory()) {
|
|
652
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
653
|
+
for (const entry of fs.readdirSync(sourcePath, { withFileTypes: true })) {
|
|
654
|
+
copyPathWithDryRun(
|
|
655
|
+
path.join(sourcePath, entry.name),
|
|
656
|
+
path.join(targetPath, entry.name),
|
|
657
|
+
false,
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
664
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function removeFileOrDirectoryWithDryRun(targetPath, dryRun) {
|
|
668
|
+
if (!fs.existsSync(targetPath)) {
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
if (dryRun) {
|
|
673
|
+
logInfo(`[dry-run] remove ${targetPath}`);
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
function resolveAngularProjectTsConfig(projectRoot) {
|
|
681
|
+
const appTsConfigPath = path.join(projectRoot, 'tsconfig.app.json');
|
|
682
|
+
if (fs.existsSync(appTsConfigPath)) {
|
|
683
|
+
return appTsConfigPath;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
return path.join(projectRoot, 'tsconfig.json');
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function upsertTypescriptPluginInTsConfig(configPath, dryRun) {
|
|
690
|
+
if (!fs.existsSync(configPath)) {
|
|
691
|
+
logWarn(`TypeScript config not found: ${configPath}`);
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
const tsConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
696
|
+
const compilerOptions = tsConfig.compilerOptions ?? {};
|
|
697
|
+
const plugins = Array.isArray(compilerOptions.plugins)
|
|
698
|
+
? compilerOptions.plugins
|
|
699
|
+
: [];
|
|
700
|
+
|
|
701
|
+
if (
|
|
702
|
+
!plugins.some(
|
|
703
|
+
(plugin) =>
|
|
704
|
+
plugin &&
|
|
705
|
+
typeof plugin === 'object' &&
|
|
706
|
+
plugin.name === '@rs-x/typescript-plugin',
|
|
707
|
+
)
|
|
708
|
+
) {
|
|
709
|
+
plugins.push({ name: '@rs-x/typescript-plugin' });
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
compilerOptions.plugins = plugins;
|
|
713
|
+
tsConfig.compilerOptions = compilerOptions;
|
|
714
|
+
|
|
715
|
+
if (dryRun) {
|
|
716
|
+
logInfo(`[dry-run] patch ${configPath}`);
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
fs.writeFileSync(configPath, `${JSON.stringify(tsConfig, null, 2)}\n`, 'utf8');
|
|
721
|
+
}
|
|
722
|
+
|
|
562
723
|
function toFileDependencySpec(fromDir, targetPath) {
|
|
563
724
|
const relative = path.relative(fromDir, targetPath).replace(/\\/gu, '/');
|
|
564
725
|
const normalized = relative.startsWith('.') ? relative : `./${relative}`;
|
|
@@ -608,14 +769,17 @@ function resolveProjectRsxSpecs(
|
|
|
608
769
|
options = {},
|
|
609
770
|
) {
|
|
610
771
|
const includeAngularPackage = Boolean(options.includeAngularPackage);
|
|
772
|
+
const includeReactPackage = Boolean(options.includeReactPackage);
|
|
773
|
+
const versionSpec = options.tag ? options.tag : RSX_PACKAGE_VERSION;
|
|
611
774
|
const defaults = {
|
|
612
|
-
'@rs-x/core':
|
|
613
|
-
'@rs-x/state-manager':
|
|
614
|
-
'@rs-x/expression-parser':
|
|
615
|
-
'@rs-x/compiler':
|
|
616
|
-
'@rs-x/typescript-plugin':
|
|
617
|
-
...(includeAngularPackage ? { '@rs-x/angular':
|
|
618
|
-
'@rs-x/
|
|
775
|
+
'@rs-x/core': versionSpec,
|
|
776
|
+
'@rs-x/state-manager': versionSpec,
|
|
777
|
+
'@rs-x/expression-parser': versionSpec,
|
|
778
|
+
'@rs-x/compiler': versionSpec,
|
|
779
|
+
'@rs-x/typescript-plugin': versionSpec,
|
|
780
|
+
...(includeAngularPackage ? { '@rs-x/angular': versionSpec } : {}),
|
|
781
|
+
...(includeReactPackage ? { '@rs-x/react': versionSpec } : {}),
|
|
782
|
+
'@rs-x/cli': versionSpec,
|
|
619
783
|
};
|
|
620
784
|
|
|
621
785
|
const tarballSlugs = {
|
|
@@ -625,6 +789,7 @@ function resolveProjectRsxSpecs(
|
|
|
625
789
|
'@rs-x/compiler': 'rs-x-compiler',
|
|
626
790
|
'@rs-x/typescript-plugin': 'rs-x-typescript-plugin',
|
|
627
791
|
...(includeAngularPackage ? { '@rs-x/angular': 'rs-x-angular' } : {}),
|
|
792
|
+
...(includeReactPackage ? { '@rs-x/react': 'rs-x-react' } : {}),
|
|
628
793
|
'@rs-x/cli': 'rs-x-cli',
|
|
629
794
|
};
|
|
630
795
|
|
|
@@ -647,6 +812,11 @@ function resolveProjectRsxSpecs(
|
|
|
647
812
|
'rs-x-angular': path.join(tarballsDir, 'rs-x-angular'),
|
|
648
813
|
}
|
|
649
814
|
: {}),
|
|
815
|
+
...(includeReactPackage
|
|
816
|
+
? {
|
|
817
|
+
'rs-x-react': path.join(tarballsDir, 'rs-x-react'),
|
|
818
|
+
}
|
|
819
|
+
: {}),
|
|
650
820
|
'rs-x-cli': path.join(tarballsDir, 'rs-x-cli'),
|
|
651
821
|
};
|
|
652
822
|
|
|
@@ -690,6 +860,11 @@ function resolveProjectRsxSpecs(
|
|
|
690
860
|
),
|
|
691
861
|
}
|
|
692
862
|
: {}),
|
|
863
|
+
...(includeReactPackage
|
|
864
|
+
? {
|
|
865
|
+
'@rs-x/react': path.join(workspaceRoot, 'rs-x-react'),
|
|
866
|
+
}
|
|
867
|
+
: {}),
|
|
693
868
|
'@rs-x/cli': path.join(workspaceRoot, 'rs-x-cli'),
|
|
694
869
|
};
|
|
695
870
|
|
|
@@ -919,6 +1094,7 @@ async function runProject(flags) {
|
|
|
919
1094
|
const dryRun = Boolean(flags['dry-run']);
|
|
920
1095
|
const skipInstall = Boolean(flags['skip-install']);
|
|
921
1096
|
const pm = detectPackageManager(flags.pm);
|
|
1097
|
+
const tag = resolveInstallTag(flags);
|
|
922
1098
|
let projectName = typeof flags.name === 'string' ? flags.name.trim() : '';
|
|
923
1099
|
|
|
924
1100
|
if (!projectName) {
|
|
@@ -946,6 +1122,7 @@ async function runProject(flags) {
|
|
|
946
1122
|
projectRoot,
|
|
947
1123
|
workspaceRoot,
|
|
948
1124
|
tarballsDir,
|
|
1125
|
+
{ tag },
|
|
949
1126
|
);
|
|
950
1127
|
if (fs.existsSync(projectRoot) && fs.readdirSync(projectRoot).length > 0) {
|
|
951
1128
|
logError(`Target directory is not empty: ${projectRoot}`);
|
|
@@ -1153,6 +1330,315 @@ function scaffoldProjectTemplate(template, projectName, pm, flags) {
|
|
|
1153
1330
|
process.exit(1);
|
|
1154
1331
|
}
|
|
1155
1332
|
|
|
1333
|
+
function applyAngularDemoStarter(projectRoot, projectName, pm, flags) {
|
|
1334
|
+
const dryRun = Boolean(flags['dry-run']);
|
|
1335
|
+
const tag = resolveInstallTag(flags);
|
|
1336
|
+
const tarballsDir =
|
|
1337
|
+
typeof flags['tarballs-dir'] === 'string'
|
|
1338
|
+
? path.resolve(process.cwd(), flags['tarballs-dir'])
|
|
1339
|
+
: typeof process.env.RSX_TARBALLS_DIR === 'string' &&
|
|
1340
|
+
process.env.RSX_TARBALLS_DIR.trim().length > 0
|
|
1341
|
+
? path.resolve(process.cwd(), process.env.RSX_TARBALLS_DIR)
|
|
1342
|
+
: null;
|
|
1343
|
+
const workspaceRoot = findRepoRoot(projectRoot);
|
|
1344
|
+
const rsxSpecs = resolveProjectRsxSpecs(
|
|
1345
|
+
projectRoot,
|
|
1346
|
+
workspaceRoot,
|
|
1347
|
+
tarballsDir,
|
|
1348
|
+
{ tag, includeAngularPackage: true },
|
|
1349
|
+
);
|
|
1350
|
+
|
|
1351
|
+
const templateFiles = ['README.md', 'src'];
|
|
1352
|
+
for (const entry of templateFiles) {
|
|
1353
|
+
copyPathWithDryRun(
|
|
1354
|
+
path.join(ANGULAR_DEMO_TEMPLATE_DIR, entry),
|
|
1355
|
+
path.join(projectRoot, entry),
|
|
1356
|
+
dryRun,
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
const staleAngularFiles = [
|
|
1361
|
+
path.join(projectRoot, 'src/app/app.ts'),
|
|
1362
|
+
path.join(projectRoot, 'src/app/app.spec.ts'),
|
|
1363
|
+
path.join(projectRoot, 'src/app/app.html'),
|
|
1364
|
+
path.join(projectRoot, 'src/app/app.css'),
|
|
1365
|
+
path.join(projectRoot, 'src/app/app.routes.ts'),
|
|
1366
|
+
path.join(projectRoot, 'src/app/app.config.ts'),
|
|
1367
|
+
];
|
|
1368
|
+
for (const stalePath of staleAngularFiles) {
|
|
1369
|
+
removeFileOrDirectoryWithDryRun(stalePath, dryRun);
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
const readmePath = path.join(projectRoot, 'README.md');
|
|
1373
|
+
if (fs.existsSync(readmePath)) {
|
|
1374
|
+
const readmeSource = fs.readFileSync(readmePath, 'utf8');
|
|
1375
|
+
const nextReadme = readmeSource.replace(
|
|
1376
|
+
/^#\s+rsx-angular-example/mu,
|
|
1377
|
+
`# ${projectName}`,
|
|
1378
|
+
);
|
|
1379
|
+
if (dryRun) {
|
|
1380
|
+
logInfo(`[dry-run] patch ${readmePath}`);
|
|
1381
|
+
} else {
|
|
1382
|
+
fs.writeFileSync(readmePath, nextReadme, 'utf8');
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
1387
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
1388
|
+
logError(
|
|
1389
|
+
`package.json not found in generated Angular app: ${packageJsonPath}`,
|
|
1390
|
+
);
|
|
1391
|
+
process.exit(1);
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
1395
|
+
const angularTsConfigPath = resolveAngularProjectTsConfig(projectRoot);
|
|
1396
|
+
const angularTsConfigRelative = path
|
|
1397
|
+
.relative(projectRoot, angularTsConfigPath)
|
|
1398
|
+
.replace(/\\/gu, '/');
|
|
1399
|
+
packageJson.name = projectName;
|
|
1400
|
+
packageJson.private = true;
|
|
1401
|
+
packageJson.version = '0.1.0';
|
|
1402
|
+
packageJson.scripts = {
|
|
1403
|
+
'build:rsx': `rsx build --project ${angularTsConfigRelative} --no-emit --prod`,
|
|
1404
|
+
'typecheck:rsx': `rsx typecheck --project ${angularTsConfigRelative}`,
|
|
1405
|
+
prebuild: 'npm run build:rsx',
|
|
1406
|
+
start: 'npm run build:rsx && ng serve',
|
|
1407
|
+
build: 'ng build',
|
|
1408
|
+
};
|
|
1409
|
+
packageJson.rsx = {
|
|
1410
|
+
build: {
|
|
1411
|
+
preparse: true,
|
|
1412
|
+
preparseFile: 'src/rsx-generated/rsx-aot-preparsed.generated.ts',
|
|
1413
|
+
compiled: true,
|
|
1414
|
+
compiledFile: 'src/rsx-generated/rsx-aot-compiled.generated.ts',
|
|
1415
|
+
registrationFile: 'src/rsx-generated/rsx-aot-registration.generated.ts',
|
|
1416
|
+
compiledResolvedEvaluator: false,
|
|
1417
|
+
},
|
|
1418
|
+
};
|
|
1419
|
+
packageJson.dependencies = {
|
|
1420
|
+
...(packageJson.dependencies ?? {}),
|
|
1421
|
+
'@rs-x/angular': rsxSpecs['@rs-x/angular'],
|
|
1422
|
+
'@rs-x/core': rsxSpecs['@rs-x/core'],
|
|
1423
|
+
'@rs-x/state-manager': rsxSpecs['@rs-x/state-manager'],
|
|
1424
|
+
'@rs-x/expression-parser': rsxSpecs['@rs-x/expression-parser'],
|
|
1425
|
+
};
|
|
1426
|
+
packageJson.devDependencies = {
|
|
1427
|
+
...(packageJson.devDependencies ?? {}),
|
|
1428
|
+
'@rs-x/cli': rsxSpecs['@rs-x/cli'],
|
|
1429
|
+
'@rs-x/compiler': rsxSpecs['@rs-x/compiler'],
|
|
1430
|
+
'@rs-x/typescript-plugin': rsxSpecs['@rs-x/typescript-plugin'],
|
|
1431
|
+
};
|
|
1432
|
+
|
|
1433
|
+
if (dryRun) {
|
|
1434
|
+
logInfo(`[dry-run] patch ${packageJsonPath}`);
|
|
1435
|
+
} else {
|
|
1436
|
+
fs.writeFileSync(
|
|
1437
|
+
packageJsonPath,
|
|
1438
|
+
`${JSON.stringify(packageJson, null, 2)}\n`,
|
|
1439
|
+
'utf8',
|
|
1440
|
+
);
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
const angularJsonPath = path.join(projectRoot, 'angular.json');
|
|
1444
|
+
if (!fs.existsSync(angularJsonPath)) {
|
|
1445
|
+
logError(
|
|
1446
|
+
`angular.json not found in generated Angular app: ${angularJsonPath}`,
|
|
1447
|
+
);
|
|
1448
|
+
process.exit(1);
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
const angularJson = JSON.parse(fs.readFileSync(angularJsonPath, 'utf8'));
|
|
1452
|
+
const projects = angularJson.projects ?? {};
|
|
1453
|
+
const [angularProjectName] = Object.keys(projects);
|
|
1454
|
+
if (!angularProjectName) {
|
|
1455
|
+
logError('Generated angular.json does not define any projects.');
|
|
1456
|
+
process.exit(1);
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
const angularProject = projects[angularProjectName];
|
|
1460
|
+
const architect = angularProject.architect ?? angularProject.targets;
|
|
1461
|
+
const build = architect?.build;
|
|
1462
|
+
if (!build) {
|
|
1463
|
+
logError('Generated Angular project is missing a build target.');
|
|
1464
|
+
process.exit(1);
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
const buildOptions = build.options ?? {};
|
|
1468
|
+
const styles = Array.isArray(buildOptions.styles) ? buildOptions.styles : [];
|
|
1469
|
+
if (!styles.includes('src/styles.css')) {
|
|
1470
|
+
styles.push('src/styles.css');
|
|
1471
|
+
}
|
|
1472
|
+
buildOptions.styles = styles;
|
|
1473
|
+
buildOptions.preserveSymlinks = true;
|
|
1474
|
+
|
|
1475
|
+
const registrationFile =
|
|
1476
|
+
'src/rsx-generated/rsx-aot-registration.generated.ts';
|
|
1477
|
+
let polyfills = buildOptions.polyfills;
|
|
1478
|
+
if (typeof polyfills === 'string') {
|
|
1479
|
+
polyfills = [polyfills];
|
|
1480
|
+
} else if (!Array.isArray(polyfills)) {
|
|
1481
|
+
polyfills = [];
|
|
1482
|
+
}
|
|
1483
|
+
if (!polyfills.includes(registrationFile)) {
|
|
1484
|
+
polyfills.push(registrationFile);
|
|
1485
|
+
}
|
|
1486
|
+
buildOptions.polyfills = polyfills;
|
|
1487
|
+
build.options = buildOptions;
|
|
1488
|
+
|
|
1489
|
+
if (build.configurations?.production?.budgets) {
|
|
1490
|
+
delete build.configurations.production.budgets;
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
if (dryRun) {
|
|
1494
|
+
logInfo(`[dry-run] patch ${angularJsonPath}`);
|
|
1495
|
+
} else {
|
|
1496
|
+
fs.writeFileSync(
|
|
1497
|
+
angularJsonPath,
|
|
1498
|
+
`${JSON.stringify(angularJson, null, 2)}\n`,
|
|
1499
|
+
'utf8',
|
|
1500
|
+
);
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
if (!Boolean(flags['skip-install'])) {
|
|
1504
|
+
logInfo(`Refreshing ${pm} dependencies for the RS-X Angular starter...`);
|
|
1505
|
+
run(pm, ['install'], { dryRun });
|
|
1506
|
+
logOk('Angular starter dependencies are up to date.');
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
function applyReactDemoStarter(projectRoot, projectName, pm, flags) {
|
|
1511
|
+
const dryRun = Boolean(flags['dry-run']);
|
|
1512
|
+
const tag = resolveInstallTag(flags);
|
|
1513
|
+
const tarballsDir =
|
|
1514
|
+
typeof flags['tarballs-dir'] === 'string'
|
|
1515
|
+
? path.resolve(process.cwd(), flags['tarballs-dir'])
|
|
1516
|
+
: typeof process.env.RSX_TARBALLS_DIR === 'string' &&
|
|
1517
|
+
process.env.RSX_TARBALLS_DIR.trim().length > 0
|
|
1518
|
+
? path.resolve(process.cwd(), process.env.RSX_TARBALLS_DIR)
|
|
1519
|
+
: null;
|
|
1520
|
+
const workspaceRoot = findRepoRoot(projectRoot);
|
|
1521
|
+
const rsxSpecs = resolveProjectRsxSpecs(
|
|
1522
|
+
projectRoot,
|
|
1523
|
+
workspaceRoot,
|
|
1524
|
+
tarballsDir,
|
|
1525
|
+
{ tag, includeReactPackage: true },
|
|
1526
|
+
);
|
|
1527
|
+
|
|
1528
|
+
const templateFiles = [
|
|
1529
|
+
'README.md',
|
|
1530
|
+
'index.html',
|
|
1531
|
+
'src',
|
|
1532
|
+
'tsconfig.json',
|
|
1533
|
+
'vite.config.ts',
|
|
1534
|
+
];
|
|
1535
|
+
for (const entry of templateFiles) {
|
|
1536
|
+
copyPathWithDryRun(
|
|
1537
|
+
path.join(REACT_DEMO_TEMPLATE_DIR, entry),
|
|
1538
|
+
path.join(projectRoot, entry),
|
|
1539
|
+
dryRun,
|
|
1540
|
+
);
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
const staleReactFiles = [
|
|
1544
|
+
path.join(projectRoot, 'src/App.tsx'),
|
|
1545
|
+
path.join(projectRoot, 'src/App.css'),
|
|
1546
|
+
path.join(projectRoot, 'src/index.css'),
|
|
1547
|
+
path.join(projectRoot, 'src/vite-env.d.ts'),
|
|
1548
|
+
path.join(projectRoot, 'src/assets'),
|
|
1549
|
+
path.join(projectRoot, 'public'),
|
|
1550
|
+
path.join(projectRoot, 'eslint.config.js'),
|
|
1551
|
+
path.join(projectRoot, 'eslint.config.ts'),
|
|
1552
|
+
path.join(projectRoot, 'tsconfig.app.json'),
|
|
1553
|
+
path.join(projectRoot, 'tsconfig.node.json'),
|
|
1554
|
+
];
|
|
1555
|
+
for (const stalePath of staleReactFiles) {
|
|
1556
|
+
removeFileOrDirectoryWithDryRun(stalePath, dryRun);
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
const readmePath = path.join(projectRoot, 'README.md');
|
|
1560
|
+
if (fs.existsSync(readmePath)) {
|
|
1561
|
+
const readmeSource = fs.readFileSync(readmePath, 'utf8');
|
|
1562
|
+
const nextReadme = readmeSource.replace(
|
|
1563
|
+
/^#\s+rsx-react-example/mu,
|
|
1564
|
+
`# ${projectName}`,
|
|
1565
|
+
);
|
|
1566
|
+
if (dryRun) {
|
|
1567
|
+
logInfo(`[dry-run] patch ${readmePath}`);
|
|
1568
|
+
} else {
|
|
1569
|
+
fs.writeFileSync(readmePath, nextReadme, 'utf8');
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
1574
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
1575
|
+
logError(`package.json not found in generated React app: ${packageJsonPath}`);
|
|
1576
|
+
process.exit(1);
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
1580
|
+
packageJson.name = projectName;
|
|
1581
|
+
packageJson.private = true;
|
|
1582
|
+
packageJson.version = '0.1.0';
|
|
1583
|
+
packageJson.type = 'module';
|
|
1584
|
+
packageJson.scripts = {
|
|
1585
|
+
'build:rsx': 'rsx build --project tsconfig.json --no-emit --prod',
|
|
1586
|
+
dev: 'npm run build:rsx && vite',
|
|
1587
|
+
build: 'npm run build:rsx && vite build',
|
|
1588
|
+
preview: 'vite preview',
|
|
1589
|
+
};
|
|
1590
|
+
packageJson.rsx = {
|
|
1591
|
+
build: {
|
|
1592
|
+
preparse: true,
|
|
1593
|
+
preparseFile: 'src/rsx-generated/rsx-aot-preparsed.generated.ts',
|
|
1594
|
+
compiled: true,
|
|
1595
|
+
compiledFile: 'src/rsx-generated/rsx-aot-compiled.generated.ts',
|
|
1596
|
+
compiledResolvedEvaluator: false,
|
|
1597
|
+
},
|
|
1598
|
+
};
|
|
1599
|
+
packageJson.dependencies = {
|
|
1600
|
+
react: packageJson.dependencies?.react ?? '^19.2.4',
|
|
1601
|
+
'react-dom': packageJson.dependencies?.['react-dom'] ?? '^19.2.4',
|
|
1602
|
+
'@rs-x/core': rsxSpecs['@rs-x/core'],
|
|
1603
|
+
'@rs-x/state-manager': rsxSpecs['@rs-x/state-manager'],
|
|
1604
|
+
'@rs-x/expression-parser': rsxSpecs['@rs-x/expression-parser'],
|
|
1605
|
+
'@rs-x/react': rsxSpecs['@rs-x/react'],
|
|
1606
|
+
};
|
|
1607
|
+
packageJson.devDependencies = {
|
|
1608
|
+
typescript: packageJson.devDependencies?.typescript ?? '^5.9.3',
|
|
1609
|
+
vite: packageJson.devDependencies?.vite ?? '^7.3.1',
|
|
1610
|
+
'@vitejs/plugin-react':
|
|
1611
|
+
packageJson.devDependencies?.['@vitejs/plugin-react'] ?? '^5.1.4',
|
|
1612
|
+
'@types/react': packageJson.devDependencies?.['@types/react'] ?? '^19.2.2',
|
|
1613
|
+
'@types/react-dom':
|
|
1614
|
+
packageJson.devDependencies?.['@types/react-dom'] ?? '^19.2.2',
|
|
1615
|
+
'@rs-x/cli': rsxSpecs['@rs-x/cli'],
|
|
1616
|
+
'@rs-x/compiler': rsxSpecs['@rs-x/compiler'],
|
|
1617
|
+
'@rs-x/typescript-plugin': rsxSpecs['@rs-x/typescript-plugin'],
|
|
1618
|
+
};
|
|
1619
|
+
|
|
1620
|
+
if (dryRun) {
|
|
1621
|
+
logInfo(`[dry-run] patch ${packageJsonPath}`);
|
|
1622
|
+
} else {
|
|
1623
|
+
fs.writeFileSync(
|
|
1624
|
+
packageJsonPath,
|
|
1625
|
+
`${JSON.stringify(packageJson, null, 2)}\n`,
|
|
1626
|
+
'utf8',
|
|
1627
|
+
);
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
const tsConfigPath = path.join(projectRoot, 'tsconfig.json');
|
|
1631
|
+
if (fs.existsSync(tsConfigPath)) {
|
|
1632
|
+
upsertTypescriptPluginInTsConfig(tsConfigPath, dryRun);
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
if (!Boolean(flags['skip-install'])) {
|
|
1636
|
+
logInfo(`Refreshing ${pm} dependencies for the RS-X React starter...`);
|
|
1637
|
+
run(pm, ['install'], { dryRun });
|
|
1638
|
+
logOk('React starter dependencies are up to date.');
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1156
1642
|
async function runProjectWithTemplate(template, flags) {
|
|
1157
1643
|
const normalizedTemplate = normalizeProjectTemplate(template);
|
|
1158
1644
|
if (!normalizedTemplate) {
|
|
@@ -1184,14 +1670,11 @@ async function runProjectWithTemplate(template, flags) {
|
|
|
1184
1670
|
|
|
1185
1671
|
withWorkingDirectory(projectRoot, () => {
|
|
1186
1672
|
if (normalizedTemplate === 'angular') {
|
|
1187
|
-
|
|
1673
|
+
applyAngularDemoStarter(projectRoot, projectName, pm, flags);
|
|
1188
1674
|
return;
|
|
1189
1675
|
}
|
|
1190
1676
|
if (normalizedTemplate === 'react') {
|
|
1191
|
-
|
|
1192
|
-
...flags,
|
|
1193
|
-
entry: flags.entry ?? 'src/main.tsx',
|
|
1194
|
-
});
|
|
1677
|
+
applyReactDemoStarter(projectRoot, projectName, pm, flags);
|
|
1195
1678
|
return;
|
|
1196
1679
|
}
|
|
1197
1680
|
if (normalizedTemplate === 'nextjs') {
|
|
@@ -1700,11 +2183,12 @@ function runInit(flags) {
|
|
|
1700
2183
|
const skipVscode = Boolean(flags['skip-vscode']);
|
|
1701
2184
|
const skipInstall = Boolean(flags['skip-install']);
|
|
1702
2185
|
const pm = detectPackageManager(flags.pm);
|
|
2186
|
+
const tag = resolveInstallTag(flags);
|
|
1703
2187
|
const projectRoot = process.cwd();
|
|
1704
2188
|
|
|
1705
2189
|
if (!skipInstall) {
|
|
1706
|
-
installRuntimePackages(pm, dryRun);
|
|
1707
|
-
installCompilerPackages(pm, dryRun);
|
|
2190
|
+
installRuntimePackages(pm, dryRun, tag);
|
|
2191
|
+
installCompilerPackages(pm, dryRun, tag);
|
|
1708
2192
|
} else {
|
|
1709
2193
|
logInfo('Skipping package installation (--skip-install).');
|
|
1710
2194
|
}
|
|
@@ -1758,11 +2242,112 @@ function runInit(flags) {
|
|
|
1758
2242
|
}
|
|
1759
2243
|
}
|
|
1760
2244
|
|
|
1761
|
-
|
|
1762
|
-
|
|
2245
|
+
logOk('RS-X init completed.');
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
function upsertRsxBuildConfigInPackageJson(projectRoot, dryRun) {
|
|
2249
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
2250
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
2251
|
+
return false;
|
|
1763
2252
|
}
|
|
1764
2253
|
|
|
1765
|
-
|
|
2254
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
2255
|
+
const currentRsx = packageJson.rsx ?? {};
|
|
2256
|
+
const currentBuild = currentRsx.build ?? {};
|
|
2257
|
+
const nextBuild = {
|
|
2258
|
+
preparse: true,
|
|
2259
|
+
preparseFile: 'src/rsx-generated/rsx-aot-preparsed.generated.ts',
|
|
2260
|
+
compiled: true,
|
|
2261
|
+
compiledFile: 'src/rsx-generated/rsx-aot-compiled.generated.ts',
|
|
2262
|
+
registrationFile: 'src/rsx-generated/rsx-aot-registration.generated.ts',
|
|
2263
|
+
compiledResolvedEvaluator: false,
|
|
2264
|
+
...currentBuild,
|
|
2265
|
+
};
|
|
2266
|
+
|
|
2267
|
+
const nextPackageJson = {
|
|
2268
|
+
...packageJson,
|
|
2269
|
+
rsx: {
|
|
2270
|
+
...currentRsx,
|
|
2271
|
+
build: nextBuild,
|
|
2272
|
+
},
|
|
2273
|
+
};
|
|
2274
|
+
|
|
2275
|
+
if (dryRun) {
|
|
2276
|
+
logInfo(`[dry-run] patch ${packageJsonPath} (rsx.build)`);
|
|
2277
|
+
return true;
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
fs.writeFileSync(
|
|
2281
|
+
packageJsonPath,
|
|
2282
|
+
`${JSON.stringify(nextPackageJson, null, 2)}\n`,
|
|
2283
|
+
'utf8',
|
|
2284
|
+
);
|
|
2285
|
+
logOk(`Patched ${packageJsonPath} (rsx.build)`);
|
|
2286
|
+
return true;
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
function ensureAngularProvidersInEntry(entryFile, dryRun) {
|
|
2290
|
+
if (!fs.existsSync(entryFile)) {
|
|
2291
|
+
return false;
|
|
2292
|
+
}
|
|
2293
|
+
|
|
2294
|
+
const original = fs.readFileSync(entryFile, 'utf8');
|
|
2295
|
+
if (original.includes('providexRsx')) {
|
|
2296
|
+
logInfo(`Angular entry already includes providexRsx: ${entryFile}`);
|
|
2297
|
+
return true;
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
if (!original.includes('bootstrapApplication(')) {
|
|
2301
|
+
logWarn(
|
|
2302
|
+
`Could not automatically patch Angular providers in ${entryFile}. Expected bootstrapApplication(...).`,
|
|
2303
|
+
);
|
|
2304
|
+
logInfo(
|
|
2305
|
+
"Manual setup: import { providexRsx } from '@rs-x/angular' and add providers: [...providexRsx()] to bootstrapApplication(...).",
|
|
2306
|
+
);
|
|
2307
|
+
return false;
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
const sourceWithImport = injectImport(
|
|
2311
|
+
original,
|
|
2312
|
+
"import { providexRsx } from '@rs-x/angular';",
|
|
2313
|
+
);
|
|
2314
|
+
|
|
2315
|
+
let updated = sourceWithImport;
|
|
2316
|
+
if (
|
|
2317
|
+
/bootstrapApplication\([\s\S]*?,\s*\{[\s\S]*?providers\s*:/mu.test(updated)
|
|
2318
|
+
) {
|
|
2319
|
+
updated = updated.replace(
|
|
2320
|
+
/providers\s*:\s*\[/mu,
|
|
2321
|
+
'providers: [...providexRsx(), ',
|
|
2322
|
+
);
|
|
2323
|
+
} else if (/bootstrapApplication\([\s\S]*?,\s*\{/mu.test(updated)) {
|
|
2324
|
+
updated = updated.replace(
|
|
2325
|
+
/bootstrapApplication\(([\s\S]*?),\s*\{/mu,
|
|
2326
|
+
'bootstrapApplication($1, {\n providers: [...providexRsx()],',
|
|
2327
|
+
);
|
|
2328
|
+
} else {
|
|
2329
|
+
updated = updated.replace(
|
|
2330
|
+
/bootstrapApplication\(([\s\S]*?)\)\s*(?:\.catch\([\s\S]*?\))?\s*;/mu,
|
|
2331
|
+
'bootstrapApplication($1, {\n providers: [...providexRsx()],\n}).catch((error) => {\n console.error(error);\n});',
|
|
2332
|
+
);
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
if (updated === sourceWithImport) {
|
|
2336
|
+
logWarn(`Could not automatically inject providexRsx into ${entryFile}.`);
|
|
2337
|
+
logInfo(
|
|
2338
|
+
"Manual setup: import { providexRsx } from '@rs-x/angular' and add providers: [...providexRsx()] to bootstrapApplication(...).",
|
|
2339
|
+
);
|
|
2340
|
+
return false;
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
if (dryRun) {
|
|
2344
|
+
logInfo(`[dry-run] patch ${entryFile} (providexRsx)`);
|
|
2345
|
+
return true;
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
fs.writeFileSync(entryFile, updated, 'utf8');
|
|
2349
|
+
logOk(`Patched ${entryFile} to include providexRsx.`);
|
|
2350
|
+
return true;
|
|
1766
2351
|
}
|
|
1767
2352
|
|
|
1768
2353
|
function upsertScriptInPackageJson(
|
|
@@ -2124,95 +2709,15 @@ ${patchBlock}
|
|
|
2124
2709
|
logOk(`Patched ${nextConfigJs} with RS-X webpack loader.`);
|
|
2125
2710
|
}
|
|
2126
2711
|
|
|
2127
|
-
function wireRsxAngularWebpack(projectRoot, dryRun) {
|
|
2128
|
-
const angularJsonPath = path.join(projectRoot, 'angular.json');
|
|
2129
|
-
if (!fs.existsSync(angularJsonPath)) {
|
|
2130
|
-
logWarn('angular.json not found. Skipping Angular build integration.');
|
|
2131
|
-
return;
|
|
2132
|
-
}
|
|
2133
|
-
|
|
2134
|
-
createRsxWebpackLoaderFile(projectRoot, dryRun);
|
|
2135
|
-
|
|
2136
|
-
const webpackConfigPath = path.join(projectRoot, 'rsx-angular-webpack.cjs');
|
|
2137
|
-
const webpackConfigSource = `const path = require('node:path');
|
|
2138
|
-
|
|
2139
|
-
module.exports = {
|
|
2140
|
-
module: {
|
|
2141
|
-
rules: [
|
|
2142
|
-
{
|
|
2143
|
-
test: /\\.[jt]sx?$/u,
|
|
2144
|
-
exclude: /node_modules/u,
|
|
2145
|
-
use: [
|
|
2146
|
-
{
|
|
2147
|
-
loader: path.resolve(__dirname, './rsx-webpack-loader.cjs'),
|
|
2148
|
-
},
|
|
2149
|
-
],
|
|
2150
|
-
},
|
|
2151
|
-
],
|
|
2152
|
-
},
|
|
2153
|
-
};
|
|
2154
|
-
`;
|
|
2155
|
-
|
|
2156
|
-
if (dryRun) {
|
|
2157
|
-
logInfo(`[dry-run] create ${webpackConfigPath}`);
|
|
2158
|
-
} else {
|
|
2159
|
-
fs.writeFileSync(webpackConfigPath, webpackConfigSource, 'utf8');
|
|
2160
|
-
logOk(`Created ${webpackConfigPath}`);
|
|
2161
|
-
}
|
|
2162
|
-
|
|
2163
|
-
const angularJson = JSON.parse(fs.readFileSync(angularJsonPath, 'utf8'));
|
|
2164
|
-
const projects = angularJson.projects ?? {};
|
|
2165
|
-
const projectNames = Object.keys(projects);
|
|
2166
|
-
if (projectNames.length === 0) {
|
|
2167
|
-
logWarn('No Angular projects found in angular.json.');
|
|
2168
|
-
return;
|
|
2169
|
-
}
|
|
2170
|
-
|
|
2171
|
-
const patchPath = 'rsx-angular-webpack.cjs';
|
|
2172
|
-
for (const projectName of projectNames) {
|
|
2173
|
-
const project = projects[projectName];
|
|
2174
|
-
const architect = project.architect ?? project.targets;
|
|
2175
|
-
if (!architect?.build) {
|
|
2176
|
-
continue;
|
|
2177
|
-
}
|
|
2178
|
-
|
|
2179
|
-
const build = architect.build;
|
|
2180
|
-
if (build.builder !== '@angular-builders/custom-webpack:browser') {
|
|
2181
|
-
build.builder = '@angular-builders/custom-webpack:browser';
|
|
2182
|
-
}
|
|
2183
|
-
build.options = build.options ?? {};
|
|
2184
|
-
build.options.customWebpackConfig = build.options.customWebpackConfig ?? {};
|
|
2185
|
-
build.options.customWebpackConfig.path = patchPath;
|
|
2186
|
-
|
|
2187
|
-
if (architect.serve) {
|
|
2188
|
-
const serve = architect.serve;
|
|
2189
|
-
if (serve.builder !== '@angular-builders/custom-webpack:dev-server') {
|
|
2190
|
-
serve.builder = '@angular-builders/custom-webpack:dev-server';
|
|
2191
|
-
}
|
|
2192
|
-
serve.options = serve.options ?? {};
|
|
2193
|
-
serve.options.buildTarget =
|
|
2194
|
-
serve.options.buildTarget ?? `${projectName}:build`;
|
|
2195
|
-
serve.options.browserTarget =
|
|
2196
|
-
serve.options.browserTarget ?? `${projectName}:build`;
|
|
2197
|
-
}
|
|
2198
|
-
}
|
|
2199
|
-
|
|
2200
|
-
if (dryRun) {
|
|
2201
|
-
logInfo(`[dry-run] patch ${angularJsonPath}`);
|
|
2202
|
-
} else {
|
|
2203
|
-
fs.writeFileSync(
|
|
2204
|
-
angularJsonPath,
|
|
2205
|
-
`${JSON.stringify(angularJson, null, 2)}\n`,
|
|
2206
|
-
'utf8',
|
|
2207
|
-
);
|
|
2208
|
-
logOk(`Patched ${angularJsonPath} for RS-X Angular webpack integration.`);
|
|
2209
|
-
}
|
|
2210
|
-
}
|
|
2211
|
-
|
|
2212
2712
|
function runSetupReact(flags) {
|
|
2213
2713
|
const dryRun = Boolean(flags['dry-run']);
|
|
2214
2714
|
const pm = detectPackageManager(flags.pm);
|
|
2715
|
+
const tag = resolveInstallTag(flags);
|
|
2215
2716
|
const projectRoot = process.cwd();
|
|
2717
|
+
const angularTsConfigPath = resolveAngularProjectTsConfig(projectRoot);
|
|
2718
|
+
const angularTsConfigRelative = path
|
|
2719
|
+
.relative(projectRoot, angularTsConfigPath)
|
|
2720
|
+
.replace(/\\/gu, '/');
|
|
2216
2721
|
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
2217
2722
|
if (!fs.existsSync(packageJsonPath)) {
|
|
2218
2723
|
logError(`package.json not found in ${projectRoot}`);
|
|
@@ -2238,21 +2743,20 @@ function runSetupReact(flags) {
|
|
|
2238
2743
|
installPackages(pm, ['@rs-x/react'], {
|
|
2239
2744
|
dev: false,
|
|
2240
2745
|
dryRun,
|
|
2746
|
+
tag,
|
|
2241
2747
|
label: 'RS-X React bindings',
|
|
2242
2748
|
});
|
|
2243
2749
|
} else {
|
|
2244
2750
|
logInfo('Skipping RS-X React bindings install (--skip-install).');
|
|
2245
2751
|
}
|
|
2246
2752
|
wireRsxVitePlugin(projectRoot, dryRun);
|
|
2247
|
-
if (!Boolean(flags['skip-vscode'])) {
|
|
2248
|
-
installVsCodeExtension(flags);
|
|
2249
|
-
}
|
|
2250
2753
|
logOk('RS-X React setup completed.');
|
|
2251
2754
|
}
|
|
2252
2755
|
|
|
2253
2756
|
function runSetupNext(flags) {
|
|
2254
2757
|
const dryRun = Boolean(flags['dry-run']);
|
|
2255
2758
|
const pm = detectPackageManager(flags.pm);
|
|
2759
|
+
const tag = resolveInstallTag(flags);
|
|
2256
2760
|
runInit({
|
|
2257
2761
|
...flags,
|
|
2258
2762
|
'skip-vscode': true,
|
|
@@ -2261,21 +2765,20 @@ function runSetupNext(flags) {
|
|
|
2261
2765
|
installPackages(pm, ['@rs-x/react'], {
|
|
2262
2766
|
dev: false,
|
|
2263
2767
|
dryRun,
|
|
2768
|
+
tag,
|
|
2264
2769
|
label: 'RS-X React bindings',
|
|
2265
2770
|
});
|
|
2266
2771
|
} else {
|
|
2267
2772
|
logInfo('Skipping RS-X React bindings install (--skip-install).');
|
|
2268
2773
|
}
|
|
2269
2774
|
wireRsxNextWebpack(process.cwd(), dryRun);
|
|
2270
|
-
if (!Boolean(flags['skip-vscode'])) {
|
|
2271
|
-
installVsCodeExtension(flags);
|
|
2272
|
-
}
|
|
2273
2775
|
logOk('RS-X Next.js setup completed.');
|
|
2274
2776
|
}
|
|
2275
2777
|
|
|
2276
2778
|
function runSetupVue(flags) {
|
|
2277
2779
|
const dryRun = Boolean(flags['dry-run']);
|
|
2278
2780
|
const pm = detectPackageManager(flags.pm);
|
|
2781
|
+
const tag = resolveInstallTag(flags);
|
|
2279
2782
|
runInit({
|
|
2280
2783
|
...flags,
|
|
2281
2784
|
'skip-vscode': true,
|
|
@@ -2284,67 +2787,79 @@ function runSetupVue(flags) {
|
|
|
2284
2787
|
installPackages(pm, ['@rs-x/vue'], {
|
|
2285
2788
|
dev: false,
|
|
2286
2789
|
dryRun,
|
|
2790
|
+
tag,
|
|
2287
2791
|
label: 'RS-X Vue bindings',
|
|
2288
2792
|
});
|
|
2289
2793
|
} else {
|
|
2290
2794
|
logInfo('Skipping RS-X Vue bindings install (--skip-install).');
|
|
2291
2795
|
}
|
|
2292
2796
|
wireRsxVitePlugin(process.cwd(), dryRun);
|
|
2293
|
-
if (!Boolean(flags['skip-vscode'])) {
|
|
2294
|
-
installVsCodeExtension(flags);
|
|
2295
|
-
}
|
|
2296
2797
|
logOk('RS-X Vue setup completed.');
|
|
2297
2798
|
}
|
|
2298
2799
|
|
|
2299
2800
|
function runSetupAngular(flags) {
|
|
2300
2801
|
const dryRun = Boolean(flags['dry-run']);
|
|
2301
2802
|
const pm = detectPackageManager(flags.pm);
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
...flags,
|
|
2305
|
-
'skip-vscode': true,
|
|
2306
|
-
});
|
|
2803
|
+
const tag = resolveInstallTag(flags);
|
|
2804
|
+
const projectRoot = process.cwd();
|
|
2307
2805
|
|
|
2308
2806
|
if (!Boolean(flags['skip-install'])) {
|
|
2807
|
+
installRuntimePackages(pm, dryRun, tag);
|
|
2808
|
+
installCompilerPackages(pm, dryRun, tag);
|
|
2309
2809
|
installPackages(pm, ['@rs-x/angular'], {
|
|
2310
2810
|
dev: false,
|
|
2311
2811
|
dryRun,
|
|
2812
|
+
tag,
|
|
2312
2813
|
label: 'RS-X Angular bindings',
|
|
2313
2814
|
});
|
|
2314
|
-
installPackages(pm, ['@angular-builders/custom-webpack'], {
|
|
2315
|
-
dev: true,
|
|
2316
|
-
dryRun,
|
|
2317
|
-
label: 'Angular custom webpack builder',
|
|
2318
|
-
});
|
|
2319
2815
|
} else {
|
|
2816
|
+
logInfo('Skipping package installation (--skip-install).');
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
const entryFile = resolveEntryFile(projectRoot, 'angular', flags.entry);
|
|
2820
|
+
if (entryFile) {
|
|
2821
|
+
logInfo(`Using Angular entry file: ${entryFile}`);
|
|
2822
|
+
ensureAngularProvidersInEntry(entryFile, dryRun);
|
|
2823
|
+
} else {
|
|
2824
|
+
logWarn('Could not detect an Angular entry file automatically.');
|
|
2320
2825
|
logInfo(
|
|
2321
|
-
'
|
|
2826
|
+
'Manual setup: add providexRsx() to bootstrapApplication(...) in your main entry file.',
|
|
2322
2827
|
);
|
|
2323
2828
|
}
|
|
2324
2829
|
|
|
2325
|
-
|
|
2830
|
+
upsertRsxBuildConfigInPackageJson(projectRoot, dryRun);
|
|
2831
|
+
|
|
2326
2832
|
upsertScriptInPackageJson(
|
|
2327
|
-
|
|
2833
|
+
projectRoot,
|
|
2328
2834
|
'build:rsx',
|
|
2329
|
-
|
|
2835
|
+
`rsx build --project ${angularTsConfigRelative} --no-emit --prod`,
|
|
2330
2836
|
dryRun,
|
|
2331
2837
|
);
|
|
2332
2838
|
upsertScriptInPackageJson(
|
|
2333
|
-
|
|
2839
|
+
projectRoot,
|
|
2334
2840
|
'typecheck:rsx',
|
|
2335
|
-
|
|
2841
|
+
`rsx typecheck --project ${angularTsConfigRelative}`,
|
|
2336
2842
|
dryRun,
|
|
2337
2843
|
);
|
|
2338
2844
|
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2845
|
+
const rsxRegistrationFile = path.join(
|
|
2846
|
+
projectRoot,
|
|
2847
|
+
'src/rsx-generated/rsx-aot-registration.generated.ts',
|
|
2848
|
+
);
|
|
2849
|
+
ensureAngularPolyfillsContainsFile({
|
|
2850
|
+
projectRoot,
|
|
2851
|
+
configPath: angularTsConfigPath,
|
|
2852
|
+
filePath: rsxRegistrationFile,
|
|
2853
|
+
dryRun,
|
|
2854
|
+
});
|
|
2855
|
+
|
|
2342
2856
|
logOk('RS-X Angular setup completed.');
|
|
2343
2857
|
}
|
|
2344
2858
|
|
|
2345
2859
|
function runSetupAuto(flags) {
|
|
2346
2860
|
const projectRoot = process.cwd();
|
|
2347
2861
|
const context = detectProjectContext(projectRoot);
|
|
2862
|
+
const tag = resolveInstallTag(flags);
|
|
2348
2863
|
|
|
2349
2864
|
if (context === 'react') {
|
|
2350
2865
|
logInfo('Auto-detected framework: react');
|
|
@@ -2372,9 +2887,8 @@ function runSetupAuto(flags) {
|
|
|
2372
2887
|
|
|
2373
2888
|
logInfo('No framework-specific setup detected; running generic setup.');
|
|
2374
2889
|
const pm = detectPackageManager(flags.pm);
|
|
2375
|
-
installRuntimePackages(pm, Boolean(flags['dry-run']));
|
|
2376
|
-
installCompilerPackages(pm, Boolean(flags['dry-run']));
|
|
2377
|
-
installVsCodeExtension(flags);
|
|
2890
|
+
installRuntimePackages(pm, Boolean(flags['dry-run']), tag);
|
|
2891
|
+
installCompilerPackages(pm, Boolean(flags['dry-run']), tag);
|
|
2378
2892
|
}
|
|
2379
2893
|
|
|
2380
2894
|
function resolveProjectModule(projectRoot, moduleName) {
|
|
@@ -3142,24 +3656,27 @@ function printInstallHelp(target) {
|
|
|
3142
3656
|
if (target === 'compiler') {
|
|
3143
3657
|
console.log('Usage:');
|
|
3144
3658
|
console.log(
|
|
3145
|
-
' rsx install compiler [--pm <pnpm|npm|yarn|bun>] [--dry-run]',
|
|
3659
|
+
' rsx install compiler [--pm <pnpm|npm|yarn|bun>] [--next] [--dry-run]',
|
|
3146
3660
|
);
|
|
3147
3661
|
console.log('');
|
|
3148
3662
|
console.log('Options:');
|
|
3149
3663
|
console.log(' --pm Explicit package manager');
|
|
3664
|
+
console.log(' --next Install prerelease versions (dist-tag next)');
|
|
3150
3665
|
console.log(' --dry-run Print commands without executing them');
|
|
3151
3666
|
return;
|
|
3152
3667
|
}
|
|
3153
3668
|
|
|
3154
3669
|
console.log('Usage:');
|
|
3155
3670
|
console.log(' rsx install vscode [--force] [--local] [--dry-run]');
|
|
3156
|
-
console.log(
|
|
3671
|
+
console.log(
|
|
3672
|
+
' rsx install compiler [--pm <pnpm|npm|yarn|bun>] [--next] [--dry-run]',
|
|
3673
|
+
);
|
|
3157
3674
|
}
|
|
3158
3675
|
|
|
3159
3676
|
function printSetupHelp() {
|
|
3160
3677
|
console.log('Usage:');
|
|
3161
3678
|
console.log(
|
|
3162
|
-
' rsx setup [--pm <pnpm|npm|yarn|bun>] [--force] [--local] [--dry-run]',
|
|
3679
|
+
' rsx setup [--pm <pnpm|npm|yarn|bun>] [--next] [--force] [--local] [--dry-run]',
|
|
3163
3680
|
);
|
|
3164
3681
|
console.log('');
|
|
3165
3682
|
console.log('What it does:');
|
|
@@ -3173,6 +3690,7 @@ function printSetupHelp() {
|
|
|
3173
3690
|
console.log('');
|
|
3174
3691
|
console.log('Options:');
|
|
3175
3692
|
console.log(' --pm Explicit package manager');
|
|
3693
|
+
console.log(' --next Install prerelease versions (dist-tag next)');
|
|
3176
3694
|
console.log(' --force Reinstall extension if already installed');
|
|
3177
3695
|
console.log(' --local Build/install local VSIX from repo workspace');
|
|
3178
3696
|
console.log(' --dry-run Print commands without executing them');
|
|
@@ -3181,7 +3699,7 @@ function printSetupHelp() {
|
|
|
3181
3699
|
function printInitHelp() {
|
|
3182
3700
|
console.log('Usage:');
|
|
3183
3701
|
console.log(
|
|
3184
|
-
' rsx init [--pm <pnpm|npm|yarn|bun>] [--entry <path>] [--skip-install] [--skip-vscode] [--force] [--local] [--dry-run]',
|
|
3702
|
+
' rsx init [--pm <pnpm|npm|yarn|bun>] [--entry <path>] [--next] [--skip-install] [--skip-vscode] [--force] [--local] [--dry-run]',
|
|
3185
3703
|
);
|
|
3186
3704
|
console.log('');
|
|
3187
3705
|
console.log('What it does:');
|
|
@@ -3196,6 +3714,7 @@ function printInitHelp() {
|
|
|
3196
3714
|
console.log('Options:');
|
|
3197
3715
|
console.log(' --pm Explicit package manager');
|
|
3198
3716
|
console.log(' --entry Explicit application entry file');
|
|
3717
|
+
console.log(' --next Install prerelease versions (dist-tag next)');
|
|
3199
3718
|
console.log(' --skip-install Skip npm/pnpm/yarn/bun package installation');
|
|
3200
3719
|
console.log(' --skip-vscode Skip VS Code extension installation');
|
|
3201
3720
|
console.log(' --force Reinstall extension if already installed');
|
|
@@ -3206,18 +3725,23 @@ function printInitHelp() {
|
|
|
3206
3725
|
function printProjectHelp() {
|
|
3207
3726
|
console.log('Usage:');
|
|
3208
3727
|
console.log(
|
|
3209
|
-
' rsx project [angular|vuejs|react|nextjs|nodejs] [--name <project-name>] [--pm <pnpm|npm|yarn|bun>] [--template <angular|vuejs|react|nextjs|nodejs>] [--tarballs-dir <path>] [--skip-install] [--skip-vscode] [--dry-run]',
|
|
3728
|
+
' rsx project [angular|vuejs|react|nextjs|nodejs] [--name <project-name>] [--pm <pnpm|npm|yarn|bun>] [--next] [--template <angular|vuejs|react|nextjs|nodejs>] [--tarballs-dir <path>] [--skip-install] [--skip-vscode] [--dry-run]',
|
|
3210
3729
|
);
|
|
3211
3730
|
console.log('');
|
|
3212
3731
|
console.log('What it does:');
|
|
3213
3732
|
console.log(' - Creates a new project folder');
|
|
3214
3733
|
console.log(' - Supports templates: angular, vuejs, react, nextjs, nodejs');
|
|
3734
|
+
console.log(
|
|
3735
|
+
' - Angular generates the RS-X virtual-table demo starter on top of the latest Angular scaffold',
|
|
3736
|
+
);
|
|
3215
3737
|
console.log(' - Scaffolds framework app and wires RS-X bootstrap/setup');
|
|
3216
3738
|
console.log(' - Writes package.json with RS-X dependencies');
|
|
3217
3739
|
console.log(
|
|
3218
3740
|
' - Adds tsconfig + TypeScript plugin config for editor support',
|
|
3219
3741
|
);
|
|
3220
|
-
console.log(
|
|
3742
|
+
console.log(
|
|
3743
|
+
' - For Angular template: uses the latest Angular CLI scaffold, then applies the RS-X demo starter',
|
|
3744
|
+
);
|
|
3221
3745
|
console.log(' - For React/Next templates: also installs @rs-x/react');
|
|
3222
3746
|
console.log(' - For Vue template: also installs @rs-x/vue');
|
|
3223
3747
|
console.log(' - Installs dependencies (unless --skip-install)');
|
|
@@ -3228,6 +3752,7 @@ function printProjectHelp() {
|
|
|
3228
3752
|
' --template Project template (if omitted, asks interactively)',
|
|
3229
3753
|
);
|
|
3230
3754
|
console.log(' --pm Explicit package manager');
|
|
3755
|
+
console.log(' --next Install prerelease versions (dist-tag next)');
|
|
3231
3756
|
console.log(
|
|
3232
3757
|
' --tarballs-dir Directory containing local RS-X package tarballs (*.tgz)',
|
|
3233
3758
|
);
|
|
@@ -3449,7 +3974,8 @@ function main() {
|
|
|
3449
3974
|
|
|
3450
3975
|
if (command === 'install' && target === 'compiler') {
|
|
3451
3976
|
const pm = detectPackageManager(flags.pm);
|
|
3452
|
-
|
|
3977
|
+
const tag = resolveInstallTag(flags);
|
|
3978
|
+
installCompilerPackages(pm, Boolean(flags['dry-run']), tag);
|
|
3453
3979
|
return;
|
|
3454
3980
|
}
|
|
3455
3981
|
|