create-react-on-rails-app 16.4.0-rc.6 → 16.4.0-rc.8
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/lib/create-app.d.ts +1 -0
- package/lib/create-app.js +86 -14
- package/lib/index.js +17 -3
- package/lib/types.d.ts +1 -0
- package/lib/utils.d.ts +7 -0
- package/lib/utils.js +7 -0
- package/lib/validators.js +20 -1
- package/package.json +2 -2
package/lib/create-app.d.ts
CHANGED
package/lib/create-app.js
CHANGED
|
@@ -3,12 +3,47 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildGeneratorArgs = buildGeneratorArgs;
|
|
6
7
|
exports.validateAppName = validateAppName;
|
|
7
8
|
exports.createApp = createApp;
|
|
8
9
|
const path_1 = __importDefault(require("path"));
|
|
9
10
|
const fs_1 = __importDefault(require("fs"));
|
|
10
11
|
const utils_js_1 = require("./utils.js");
|
|
11
|
-
|
|
12
|
+
function cleanupAppDirectory(appPath, appName, cleanupSuccessMessage, cleanupFallbackMessage) {
|
|
13
|
+
if (!fs_1.default.existsSync(appPath)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
(0, utils_js_1.logInfo)(`Cleaning up "${appName}" directory...`);
|
|
17
|
+
try {
|
|
18
|
+
fs_1.default.rmSync(appPath, { recursive: true, force: true });
|
|
19
|
+
(0, utils_js_1.logInfo)(cleanupSuccessMessage);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
(0, utils_js_1.logError)(cleanupFallbackMessage);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function localGemPath(envVarName) {
|
|
26
|
+
const value = process.env[envVarName];
|
|
27
|
+
if (!value || value.trim() === '') {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
const resolvedPath = path_1.default.resolve(value);
|
|
31
|
+
if (!fs_1.default.existsSync(resolvedPath)) {
|
|
32
|
+
(0, utils_js_1.logError)(`Local gem path from ${envVarName} does not exist: ${resolvedPath}`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
return resolvedPath;
|
|
36
|
+
}
|
|
37
|
+
function bundleAddArgs(gemName, localPath, strict = true) {
|
|
38
|
+
const args = ['add', gemName];
|
|
39
|
+
if (localPath) {
|
|
40
|
+
args.push('--path', localPath);
|
|
41
|
+
}
|
|
42
|
+
else if (strict) {
|
|
43
|
+
args.push('--strict');
|
|
44
|
+
}
|
|
45
|
+
return args;
|
|
46
|
+
}
|
|
12
47
|
function buildGeneratorArgs(options) {
|
|
13
48
|
const args = [];
|
|
14
49
|
if (options.template === 'typescript') {
|
|
@@ -17,10 +52,13 @@ function buildGeneratorArgs(options) {
|
|
|
17
52
|
if (options.rspack) {
|
|
18
53
|
args.push('--rspack');
|
|
19
54
|
}
|
|
55
|
+
if (options.rsc) {
|
|
56
|
+
args.push('--rsc');
|
|
57
|
+
}
|
|
20
58
|
args.push('--ignore-warnings');
|
|
21
59
|
return args;
|
|
22
60
|
}
|
|
23
|
-
function printSuccessMessage(appName) {
|
|
61
|
+
function printSuccessMessage(appName, route) {
|
|
24
62
|
console.log('');
|
|
25
63
|
(0, utils_js_1.logSuccess)(`Created ${appName} with React on Rails!`);
|
|
26
64
|
console.log('');
|
|
@@ -28,7 +66,7 @@ function printSuccessMessage(appName) {
|
|
|
28
66
|
console.log(` cd ${appName}`);
|
|
29
67
|
console.log(' bin/dev');
|
|
30
68
|
console.log('');
|
|
31
|
-
(0, utils_js_1.logInfo)(
|
|
69
|
+
(0, utils_js_1.logInfo)(`Then visit http://localhost:3000/${route}`);
|
|
32
70
|
console.log('');
|
|
33
71
|
(0, utils_js_1.logInfo)('Documentation: https://www.shakacode.com/react-on-rails/docs/');
|
|
34
72
|
console.log('');
|
|
@@ -37,10 +75,10 @@ function validateAppName(name) {
|
|
|
37
75
|
if (!name || name.trim() === '') {
|
|
38
76
|
return { success: false, error: 'App name is required.' };
|
|
39
77
|
}
|
|
40
|
-
if (!/^[a-zA-Z0-
|
|
78
|
+
if (!/^[a-zA-Z][a-zA-Z0-9]*([_-][a-zA-Z0-9]+)*$/.test(name)) {
|
|
41
79
|
return {
|
|
42
80
|
success: false,
|
|
43
|
-
error: 'App name
|
|
81
|
+
error: 'App name must start with a letter, end with a letter or number, and may contain hyphens or underscores only between alphanumeric characters.',
|
|
44
82
|
};
|
|
45
83
|
}
|
|
46
84
|
const appPath = path_1.default.resolve(process.cwd(), name);
|
|
@@ -54,10 +92,15 @@ function validateAppName(name) {
|
|
|
54
92
|
}
|
|
55
93
|
function createApp(appName, options) {
|
|
56
94
|
const appPath = path_1.default.resolve(process.cwd(), appName);
|
|
95
|
+
const baseSteps = 3; // rails new + add react_on_rails + run generator
|
|
96
|
+
const totalSteps = baseSteps + (options.rsc ? 1 : 0);
|
|
97
|
+
let currentStep = 1;
|
|
98
|
+
const reactOnRailsGemPath = localGemPath('REACT_ON_RAILS_GEM_PATH');
|
|
99
|
+
const reactOnRailsProGemPath = options.rsc ? localGemPath('REACT_ON_RAILS_PRO_GEM_PATH') : null;
|
|
57
100
|
// Step 1: Create Rails application
|
|
58
|
-
// appName is validated by validateAppName() to be [a-zA-Z0-
|
|
101
|
+
// appName is validated by validateAppName() to be ^[a-zA-Z][a-zA-Z0-9]*([_-][a-zA-Z0-9]+)*$ only,
|
|
59
102
|
// so it's always a simple directory name safe to use with rails new.
|
|
60
|
-
(0, utils_js_1.logStep)(
|
|
103
|
+
(0, utils_js_1.logStep)(currentStep, totalSteps, 'Creating Rails application...');
|
|
61
104
|
try {
|
|
62
105
|
(0, utils_js_1.execLiveArgs)('rails', ['new', appName, '--database=postgresql', '--skip-javascript']);
|
|
63
106
|
(0, utils_js_1.logStepDone)('Rails application created');
|
|
@@ -67,12 +110,18 @@ function createApp(appName, options) {
|
|
|
67
110
|
if (error instanceof Error && error.message) {
|
|
68
111
|
console.error(`Debug info: ${error.message}`);
|
|
69
112
|
}
|
|
113
|
+
cleanupAppDirectory(appPath, appName, 'Directory removed. Fix the Rails app creation issue and rerun.', `Delete the created "${appName}" directory and rerun once the Rails app creation issue is resolved.`);
|
|
70
114
|
process.exit(1);
|
|
71
115
|
}
|
|
72
116
|
// Step 2: Add react_on_rails gem
|
|
73
|
-
|
|
117
|
+
currentStep += 1;
|
|
118
|
+
(0, utils_js_1.logStep)(currentStep, totalSteps, 'Adding react_on_rails gem...');
|
|
74
119
|
try {
|
|
75
|
-
|
|
120
|
+
const reactOnRailsArgs = bundleAddArgs('react_on_rails', reactOnRailsGemPath);
|
|
121
|
+
if (reactOnRailsGemPath) {
|
|
122
|
+
(0, utils_js_1.logInfo)(`Using local react_on_rails gem path: ${reactOnRailsGemPath}`);
|
|
123
|
+
}
|
|
124
|
+
(0, utils_js_1.execLiveArgs)('bundle', reactOnRailsArgs, appPath);
|
|
76
125
|
(0, utils_js_1.logStepDone)('react_on_rails gem added');
|
|
77
126
|
}
|
|
78
127
|
catch (error) {
|
|
@@ -80,11 +129,33 @@ function createApp(appName, options) {
|
|
|
80
129
|
if (error instanceof Error && error.message) {
|
|
81
130
|
console.error(`Debug info: ${error.message}`);
|
|
82
131
|
}
|
|
132
|
+
cleanupAppDirectory(appPath, appName, 'Directory removed. Fix gem installation access or connectivity issues and rerun.', `Delete the created "${appName}" directory and rerun once gem installation issues are resolved.`);
|
|
83
133
|
process.exit(1);
|
|
84
134
|
}
|
|
85
|
-
|
|
135
|
+
if (options.rsc) {
|
|
136
|
+
currentStep += 1;
|
|
137
|
+
(0, utils_js_1.logStep)(currentStep, totalSteps, 'Adding react_on_rails_pro gem (--rsc)...');
|
|
138
|
+
try {
|
|
139
|
+
const reactOnRailsProArgs = bundleAddArgs('react_on_rails_pro', reactOnRailsProGemPath, false);
|
|
140
|
+
if (reactOnRailsProGemPath) {
|
|
141
|
+
(0, utils_js_1.logInfo)(`Using local react_on_rails_pro gem path: ${reactOnRailsProGemPath}`);
|
|
142
|
+
}
|
|
143
|
+
(0, utils_js_1.execLiveArgs)('bundle', reactOnRailsProArgs, appPath);
|
|
144
|
+
(0, utils_js_1.logStepDone)('react_on_rails_pro gem added');
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
(0, utils_js_1.logError)('Failed to add react_on_rails_pro gem required by --rsc.');
|
|
148
|
+
if (error instanceof Error && error.message) {
|
|
149
|
+
console.error(`Debug info: ${error.message}`);
|
|
150
|
+
}
|
|
151
|
+
cleanupAppDirectory(appPath, appName, 'Directory removed. Ensure react_on_rails_pro is installable in your Bundler/RubyGems setup, then rerun with --rsc.', `Ensure react_on_rails_pro is installable, then delete the created "${appName}" directory and rerun with --rsc.`);
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Final generator step
|
|
86
156
|
const generatorArgs = buildGeneratorArgs(options);
|
|
87
|
-
|
|
157
|
+
currentStep += 1;
|
|
158
|
+
(0, utils_js_1.logStep)(currentStep, totalSteps, 'Running React on Rails generator...');
|
|
88
159
|
try {
|
|
89
160
|
(0, utils_js_1.execLiveArgs)('bundle', ['exec', 'rails', 'generate', 'react_on_rails:install', ...generatorArgs], appPath);
|
|
90
161
|
(0, utils_js_1.logStepDone)('React on Rails setup complete');
|
|
@@ -94,10 +165,11 @@ function createApp(appName, options) {
|
|
|
94
165
|
if (error instanceof Error && error.message) {
|
|
95
166
|
console.error(`Debug info: ${error.message}`);
|
|
96
167
|
}
|
|
168
|
+
cleanupAppDirectory(appPath, appName, 'Directory removed. Fix the generator issue and rerun.', `Delete the created "${appName}" directory and rerun once the generator issue is resolved.`);
|
|
97
169
|
process.exit(1);
|
|
98
170
|
}
|
|
99
|
-
//
|
|
100
|
-
(0, utils_js_1.
|
|
101
|
-
printSuccessMessage(appName);
|
|
171
|
+
// Final success
|
|
172
|
+
(0, utils_js_1.logStepDone)('Done!');
|
|
173
|
+
printSuccessMessage(appName, options.rsc ? 'hello_server' : 'hello_world');
|
|
102
174
|
}
|
|
103
175
|
//# sourceMappingURL=create-app.js.map
|
package/lib/index.js
CHANGED
|
@@ -31,6 +31,7 @@ function run(appName, rawOpts) {
|
|
|
31
31
|
template,
|
|
32
32
|
packageManager: packageManager,
|
|
33
33
|
rspack: Boolean(rawOpts.rspack),
|
|
34
|
+
rsc: Boolean(rawOpts.rsc),
|
|
34
35
|
};
|
|
35
36
|
console.log('');
|
|
36
37
|
console.log(`${chalk_1.default.bold('create-react-on-rails-app')} v${packageJson.version}`);
|
|
@@ -40,6 +41,12 @@ function run(appName, rawOpts) {
|
|
|
40
41
|
(0, utils_js_1.logError)(nameValidation.error ?? 'Invalid app name');
|
|
41
42
|
process.exit(1);
|
|
42
43
|
}
|
|
44
|
+
if (options.rsc) {
|
|
45
|
+
(0, utils_js_1.logInfo)('Note: --rsc installs react_on_rails_pro and requires that gem to be installable.');
|
|
46
|
+
(0, utils_js_1.logInfo)('If installation fails, verify your Bundler/RubyGems setup for react_on_rails_pro, then rerun with --rsc.');
|
|
47
|
+
(0, utils_js_1.logInfo)('Pro setup docs: https://www.shakacode.com/react-on-rails-pro/docs/installation/');
|
|
48
|
+
console.log('');
|
|
49
|
+
}
|
|
43
50
|
(0, utils_js_1.logInfo)('Checking prerequisites...');
|
|
44
51
|
const { allValid, results } = (0, validators_js_1.validateAll)(options.packageManager);
|
|
45
52
|
for (const { name, result } of results) {
|
|
@@ -60,7 +67,7 @@ function run(appName, rawOpts) {
|
|
|
60
67
|
process.exit(1);
|
|
61
68
|
}
|
|
62
69
|
console.log('');
|
|
63
|
-
(0, utils_js_1.logInfo)(`Creating "${appName}" with template: ${options.template}, package manager: ${options.packageManager}${options.rspack ? ', bundler: rspack' : ''}`);
|
|
70
|
+
(0, utils_js_1.logInfo)(`Creating "${appName}" with template: ${options.template}, package manager: ${options.packageManager}${options.rspack ? ', bundler: rspack' : ''}${options.rsc ? ', mode: rsc' : ''}`);
|
|
64
71
|
(0, create_app_js_1.createApp)(appName, options);
|
|
65
72
|
}
|
|
66
73
|
const program = new commander_1.Command();
|
|
@@ -74,19 +81,26 @@ program
|
|
|
74
81
|
.option('-t, --template <type>', 'javascript or typescript', 'typescript')
|
|
75
82
|
.option('-p, --package-manager <pm>', 'npm or pnpm (auto-detected if not specified)')
|
|
76
83
|
.option('--rspack', 'Use Rspack instead of Webpack (~20x faster builds)', false)
|
|
84
|
+
.option('--rsc', 'Generate React Server Components setup (installs react_on_rails_pro)', false)
|
|
77
85
|
.addHelpText('after', `
|
|
78
86
|
Examples:
|
|
79
87
|
$ npx create-react-on-rails-app my-app
|
|
80
88
|
$ npx create-react-on-rails-app my-app --template javascript
|
|
81
89
|
$ npx create-react-on-rails-app my-app --rspack
|
|
90
|
+
$ npx create-react-on-rails-app my-app --rsc
|
|
91
|
+
$ npx create-react-on-rails-app my-app --rspack --rsc
|
|
82
92
|
$ npx create-react-on-rails-app my-app --package-manager pnpm
|
|
83
93
|
|
|
84
94
|
What it does:
|
|
85
95
|
1. Creates a new Rails app with PostgreSQL
|
|
86
|
-
2. Adds
|
|
96
|
+
2. Adds required gem(s) (react_on_rails, plus react_on_rails_pro for --rsc)
|
|
87
97
|
3. Runs the React on Rails generator (Shakapacker, components, webpack config)
|
|
88
98
|
|
|
89
|
-
After setup, run bin/dev and visit
|
|
99
|
+
After setup, run bin/dev and visit:
|
|
100
|
+
- http://localhost:3000/hello_world (default)
|
|
101
|
+
- http://localhost:3000/hello_server (--rsc)
|
|
102
|
+
|
|
103
|
+
--rsc supports both JavaScript and TypeScript templates.
|
|
90
104
|
|
|
91
105
|
Documentation: https://www.shakacode.com/react-on-rails/docs/`)
|
|
92
106
|
.action((appName, opts) => {
|
package/lib/types.d.ts
CHANGED
package/lib/utils.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execute a command and stream output to the current terminal.
|
|
3
|
+
*
|
|
4
|
+
* Security contract:
|
|
5
|
+
* - This uses `spawnSync` with `shell: false`, so command and args are passed as literal tokens.
|
|
6
|
+
* - Callers must still ensure `command` and `args` come from trusted/validated values.
|
|
7
|
+
*/
|
|
1
8
|
export declare function execLiveArgs(command: string, args: string[], cwd?: string): void;
|
|
2
9
|
export declare function getCommandVersion(command: string): string | null;
|
|
3
10
|
export declare function detectPackageManager(): 'npm' | 'pnpm' | null;
|
package/lib/utils.js
CHANGED
|
@@ -13,6 +13,13 @@ exports.logSuccess = logSuccess;
|
|
|
13
13
|
exports.logInfo = logInfo;
|
|
14
14
|
const child_process_1 = require("child_process");
|
|
15
15
|
const chalk_1 = __importDefault(require("chalk"));
|
|
16
|
+
/**
|
|
17
|
+
* Execute a command and stream output to the current terminal.
|
|
18
|
+
*
|
|
19
|
+
* Security contract:
|
|
20
|
+
* - This uses `spawnSync` with `shell: false`, so command and args are passed as literal tokens.
|
|
21
|
+
* - Callers must still ensure `command` and `args` come from trusted/validated values.
|
|
22
|
+
*/
|
|
16
23
|
function execLiveArgs(command, args, cwd) {
|
|
17
24
|
const result = (0, child_process_1.spawnSync)(command, args, {
|
|
18
25
|
stdio: 'inherit',
|
package/lib/validators.js
CHANGED
|
@@ -9,6 +9,8 @@ const utils_js_1 = require("./utils.js");
|
|
|
9
9
|
const MIN_NODE_VERSION = 18;
|
|
10
10
|
const MIN_RUBY_MAJOR = 3;
|
|
11
11
|
const MIN_RUBY_MINOR = 0;
|
|
12
|
+
const MIN_RAILS_MAJOR = 7;
|
|
13
|
+
const MIN_RAILS_MINOR = 0;
|
|
12
14
|
function validateNode() {
|
|
13
15
|
const version = process.versions.node;
|
|
14
16
|
const major = parseInt(version.split('.')[0], 10);
|
|
@@ -64,7 +66,24 @@ function validateRails() {
|
|
|
64
66
|
'Then try again.',
|
|
65
67
|
};
|
|
66
68
|
}
|
|
67
|
-
|
|
69
|
+
const firstLine = railsVersion.split('\n')[0].trim();
|
|
70
|
+
const match = firstLine.match(/rails\s+(\d+)\.(\d+)/i);
|
|
71
|
+
if (!match) {
|
|
72
|
+
return {
|
|
73
|
+
valid: false,
|
|
74
|
+
message: `Could not parse Rails version from: "${firstLine}". Please ensure Rails ${MIN_RAILS_MAJOR}.${MIN_RAILS_MINOR}+ is installed.`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const major = parseInt(match[1], 10);
|
|
78
|
+
const minor = parseInt(match[2], 10);
|
|
79
|
+
// minor check is 0 now; kept for parity with Ruby validator and easy bumping later
|
|
80
|
+
if (major < MIN_RAILS_MAJOR || (major === MIN_RAILS_MAJOR && minor < MIN_RAILS_MINOR)) {
|
|
81
|
+
return {
|
|
82
|
+
valid: false,
|
|
83
|
+
message: `${firstLine} detected. React on Rails requires Rails ${MIN_RAILS_MAJOR}.${MIN_RAILS_MINOR}+.`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return { valid: true, message: firstLine };
|
|
68
87
|
}
|
|
69
88
|
function validatePackageManager(pm) {
|
|
70
89
|
const version = (0, utils_js_1.getCommandVersion)(pm);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-react-on-rails-app",
|
|
3
|
-
"version": "16.4.0-rc.
|
|
3
|
+
"version": "16.4.0-rc.8",
|
|
4
4
|
"description": "Create React on Rails applications with a single command",
|
|
5
5
|
"bin": {
|
|
6
6
|
"create-react-on-rails-app": "./bin/create-react-on-rails-app.js"
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "pnpm run clean && tsc",
|
|
46
46
|
"build-watch": "pnpm run clean && tsc --watch",
|
|
47
|
-
"clean": "
|
|
47
|
+
"clean": "node -e \"require('fs').rmSync('./lib',{recursive:true,force:true})\"",
|
|
48
48
|
"test": "jest tests",
|
|
49
49
|
"type-check": "tsc --noEmit --noErrorTruncation"
|
|
50
50
|
}
|