@rollipop/init 1.0.0-beta.1 → 1.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +12 -0
  2. package/dist/index.js +107 -15
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1 +1,13 @@
1
1
  # @rollipop/init
2
+
3
+ Run from a React Native or Expo project root:
4
+
5
+ ```bash
6
+ yarn dlx @rollipop/init@beta
7
+ yarn install
8
+ ```
9
+
10
+ - Bare projects: logs `bare project detected`, adds the matching `rollipop` version, and configures `react-native.config.js`.
11
+ - Expo projects: logs `expo project detected`, adds matching `rollipop` and `@rollipop/expo` versions, and registers the config plugin in `app.json` or `app.config.json` while preserving existing plugins. Dynamic JS/TS configuration is left unchanged with manual setup instructions.
12
+
13
+ Expo detection uses the `expo` dependency in `package.json`, including prebuilt projects with native directories. See [Quick Start](https://rollipop.dev/docs/get-started/quick-start) or the [Expo guide](https://rollipop.dev/docs/experimental/expo) for setup and limitations.
package/dist/index.js CHANGED
@@ -2,12 +2,80 @@ import pc from "picocolors";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { Visitor, parseSync } from "oxc-parser";
5
- //#region package.json
6
- var version = "1.0.0-beta.1";
7
5
  //#endregion
8
6
  //#region src/init.ts
7
+ const { version } = {
8
+ name: "@rollipop/init",
9
+ version: "1.0.0-beta.2",
10
+ homepage: "https://github.com/rollipop-dev/rollipop#readme",
11
+ bugs: { "url": "https://github.com/rollipop-dev/rollipop/issues" },
12
+ license: "MIT",
13
+ author: "leegeunhyeok <dev.ghlee@gmail.com> (https://github.com/leegeunhyeok)",
14
+ repository: {
15
+ "type": "git",
16
+ "url": "git+https://github.com/rollipop-dev/rollipop.git",
17
+ "directory": "packages/core"
18
+ },
19
+ bin: "./bin/index.js",
20
+ files: ["bin", "dist"],
21
+ type: "module",
22
+ scripts: {
23
+ "execute": "node --import @oxc-node/core/register src/index.ts",
24
+ "prepack": "yarn build",
25
+ "typecheck": "tsc --noEmit",
26
+ "test": "vp test --run --passWithNoTests",
27
+ "build": "vp pack"
28
+ },
29
+ dependencies: {
30
+ "oxc-parser": "0.151.0",
31
+ "picocolors": "1.1.1"
32
+ },
33
+ devDependencies: {
34
+ "@oxc-node/core": "^0.1.3",
35
+ "typescript": "catalog:",
36
+ "vite-plus": "catalog:vp"
37
+ },
38
+ installConfig: { "hoistingLimits": "none" }
39
+ };
9
40
  const RN_CONFIG_FILE = "react-native.config.js";
10
41
  const COMMANDS_REQUIRE = "require('rollipop/commands')";
42
+ function detectProject(cwd) {
43
+ const packageJson = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf8"));
44
+ return packageJson.dependencies?.expo || packageJson.devDependencies?.expo ? "expo" : "bare";
45
+ }
46
+ function setupExpoConfig(cwd) {
47
+ const dynamicConfig = [
48
+ "ts",
49
+ "mts",
50
+ "cts",
51
+ "mjs",
52
+ "cjs",
53
+ "js"
54
+ ].map((extension) => `app.config.${extension}`).find((filename) => fs.existsSync(path.join(cwd, filename)));
55
+ if (dynamicConfig) return {
56
+ status: "manual-required",
57
+ filename: dynamicConfig
58
+ };
59
+ const filename = ["app.config.json", "app.json"].find((name) => fs.existsSync(path.join(cwd, name))) ?? "app.json";
60
+ const configPath = path.join(cwd, filename);
61
+ const exists = fs.existsSync(configPath);
62
+ const root = exists ? JSON.parse(fs.readFileSync(configPath, "utf8")) : { expo: {} };
63
+ if (!root || typeof root !== "object" || Array.isArray(root)) throw new Error(`${filename} must contain an Expo configuration object`);
64
+ const config = "expo" in root ? root.expo : root;
65
+ if (!config || typeof config !== "object" || Array.isArray(config)) throw new Error(`${filename}: 'expo' must be an object`);
66
+ const plugins = config.plugins ?? [];
67
+ if (!Array.isArray(plugins)) throw new Error(`${filename}: 'plugins' must be an array`);
68
+ if (plugins.some((plugin) => (Array.isArray(plugin) ? plugin[0] : plugin) === "@rollipop/expo")) return {
69
+ status: "already-configured",
70
+ filename
71
+ };
72
+ config.plugins = [...plugins, "@rollipop/expo"];
73
+ fs.writeFileSync(configPath, JSON.stringify(root, null, 2) + "\n");
74
+ return {
75
+ status: exists ? "updated" : "created",
76
+ filename
77
+ };
78
+ }
11
79
  function setupReactNativeConfig(cwd) {
12
80
  const configPath = path.join(cwd, RN_CONFIG_FILE);
13
81
  if (!fs.existsSync(configPath)) {
@@ -90,11 +158,16 @@ function applyInjection(content, insertOffset, needsComma) {
90
158
  function setupPackage(cwd) {
91
159
  const packageJsonPath = path.join(cwd, "package.json");
92
160
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
161
+ const dependencies = {
162
+ rollipop: version,
163
+ ...detectProject(cwd) === "expo" ? { "@rollipop/expo": version } : {}
164
+ };
93
165
  packageJson.devDependencies = {
94
166
  ...packageJson.devDependencies,
95
- rollipop: version
167
+ ...dependencies
96
168
  };
97
169
  fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n");
170
+ return Object.keys(dependencies);
98
171
  }
99
172
  //#endregion
100
173
  //#region src/index.ts
@@ -114,43 +187,62 @@ const logger = {
114
187
  };
115
188
  console.log(`\n${pc.bold(pc.cyan("rollipop"))} ${pc.dim("— Modern build toolkit for React Native")}\n`);
116
189
  const cwd = process.cwd();
190
+ let projectType;
191
+ try {
192
+ projectType = detectProject(cwd);
193
+ } catch (error) {
194
+ logger.error("Project detection failed", error);
195
+ process.exit(1);
196
+ }
197
+ logger.info(`${projectType} project detected`);
117
198
  let failed = false;
118
199
  let hasWarning = false;
200
+ const guideUrl = projectType === "expo" ? "https://rollipop.dev/docs/experimental/expo" : "https://rollipop.dev/docs/get-started/quick-start";
119
201
  try {
120
- switch (setupReactNativeConfig(cwd)) {
202
+ const { status, filename } = projectType === "expo" ? setupExpoConfig(cwd) : {
203
+ status: setupReactNativeConfig(cwd),
204
+ filename: "react-native.config.js"
205
+ };
206
+ switch (status) {
121
207
  case "created":
122
- logger.success(`Created ${pc.bold("react-native.config.js")}`);
208
+ logger.success(`Created ${pc.bold(filename)}`);
123
209
  break;
124
210
  case "updated":
125
- logger.success(`Updated ${pc.bold("react-native.config.js")}`);
211
+ logger.success(`Updated ${pc.bold(filename)}`);
126
212
  break;
127
213
  case "already-configured":
128
- logger.info(`${pc.bold("react-native.config.js")} already configured`);
214
+ logger.info(`${pc.bold(filename)} already configured`);
129
215
  break;
130
216
  case "manual-required":
131
217
  hasWarning = true;
132
- logger.warn(`Could not auto-configure: ${pc.bold("module.exports")} is not a plain object`);
133
- console.log(` ${pc.dim("Add the following to your")} ${pc.bold("react-native.config.js")}${pc.dim(":")}\n ${pc.green("commands: require('rollipop/commands'")})`);
218
+ if (projectType === "expo") {
219
+ logger.warn(`Could not auto-configure dynamic Expo config: ${pc.bold(filename)}`);
220
+ console.log(` Add ${pc.green("'@rollipop/expo'")} to the exported config's ${pc.bold("plugins")} array, keeping existing entries.`);
221
+ } else {
222
+ logger.warn(`Could not auto-configure: ${pc.bold("module.exports")} is not a plain object`);
223
+ console.log(` ${pc.dim("Add the following to your")} ${pc.bold(filename)}${pc.dim(":")}\n ${pc.green("commands: require('rollipop/commands')")}`);
224
+ }
134
225
  }
135
226
  } catch (error) {
136
- logger.error("React Native CLI setup failed", error);
227
+ logger.error(projectType === "expo" ? "Expo setup failed" : "React Native CLI setup failed", error);
137
228
  failed ||= true;
138
229
  }
139
230
  if (hasWarning || failed) console.log();
140
231
  try {
141
- setupPackage(cwd);
142
- logger.success(`Added ${pc.bold("rollipop")} to ${pc.bold("devDependencies")}`);
232
+ const dependencies = setupPackage(cwd);
233
+ logger.success(`Added ${dependencies.map((dependency) => pc.bold(dependency)).join(", ")} to ${pc.bold("devDependencies")}`);
143
234
  } catch (error) {
144
235
  logger.error("Package setup failed", error);
145
236
  failed ||= true;
146
237
  }
147
238
  if (failed) {
148
- console.log("\n" + pc.red("Setup failed.") + ` Follow the manual guide: ${pc.underline(pc.cyan("https://rollipop.dev/docs/get-started/quick-start"))}`);
239
+ console.log("\n" + pc.red("Setup failed.") + ` Follow the manual guide: ${pc.underline(pc.cyan(guideUrl))}`);
149
240
  process.exit(1);
150
241
  }
151
- logger.success(pc.bold("Setup completed!"));
242
+ if (hasWarning) logger.warn("Manual configuration is required to finish setup.");
243
+ else logger.success(pc.bold("Setup completed!"));
152
244
  console.log();
153
245
  console.log(` ${pc.dim("Install dependencies manually to finish setup.")}`);
154
- console.log(` ${pc.dim("See")} ${pc.underline(pc.cyan("https://rollipop.dev"))} ${pc.dim("for configuration details.")}`);
246
+ console.log(` ${pc.dim("See")} ${pc.underline(pc.cyan(guideUrl))} ${pc.dim("for configuration details.")}`);
155
247
  //#endregion
156
248
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rollipop/init",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0-beta.2",
4
4
  "homepage": "https://github.com/rollipop-dev/rollipop#readme",
5
5
  "bugs": {
6
6
  "url": "https://github.com/rollipop-dev/rollipop/issues"
@@ -32,7 +32,7 @@
32
32
  "devDependencies": {
33
33
  "@oxc-node/core": "^0.1.3",
34
34
  "typescript": "7.0.2",
35
- "vite-plus": "0.3.3"
35
+ "vite-plus": "1.3.0"
36
36
  },
37
37
  "installConfig": {
38
38
  "hoistingLimits": "none"