create-sparkling-app 2.0.0 → 2.1.0-rc.0

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 CHANGED
@@ -30,6 +30,10 @@ npx create-sparkling-app@latest my-app --verbose
30
30
 
31
31
  Run `npx create-sparkling-app@latest --help` to see all available options.
32
32
 
33
+ ## Local testing
34
+
35
+ When developing inside the Sparkling monorepo, see [LOCAL_TESTING.md](./LOCAL_TESTING.md) for building this CLI, running it from the workspace, and pointing `--template` at `template/sparkling-app-template`.
36
+
33
37
  ## What's Included
34
38
 
35
39
  The generated project includes:
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
@@ -7,7 +40,7 @@ exports.ProjectBuilder = void 0;
7
40
  // Copyright (c) 2025 TikTok Pte. Ltd.
8
41
  // Licensed under the Apache License Version 2.0 that can be found in the
9
42
  // LICENSE file in the root directory of this source tree.
10
- const inquirer_1 = __importDefault(require("inquirer"));
43
+ const p = __importStar(require("@clack/prompts"));
11
44
  const node_fs_1 = __importDefault(require("node:fs"));
12
45
  const node_path_1 = __importDefault(require("node:path"));
13
46
  const logger_1 = require("../../logger");
@@ -41,24 +74,14 @@ class ProjectBuilder {
41
74
  }
42
75
  const shouldCheckEmpty = this.config.checkEmpty ?? true;
43
76
  if (shouldCheckEmpty && !this.config.override && node_fs_1.default.existsSync(this.config.targetDir) && !(0, common_1.isEmptyDir)(this.config.targetDir)) {
44
- let option;
45
- try {
46
- const answer = await inquirer_1.default.prompt([
47
- {
48
- type: 'list',
49
- name: 'choice',
50
- message: `"${node_path_1.default.basename(this.config.targetDir)}" is not empty, please choose:`,
51
- choices: [
52
- { name: 'Continue and override files', value: 'yes' },
53
- { name: 'Cancel operation', value: 'no' },
54
- ],
55
- },
56
- ]);
57
- option = (0, template_1.checkCancel)(answer.choice);
58
- }
59
- catch (error) {
60
- throw new template_1.UserCancelledError(error.message);
61
- }
77
+ const option = await p.select({
78
+ message: `"${node_path_1.default.basename(this.config.targetDir)}" is not empty, please choose:`,
79
+ options: [
80
+ { label: 'Continue and override files', value: 'yes' },
81
+ { label: 'Cancel operation', value: 'no' },
82
+ ],
83
+ });
84
+ (0, template_1.checkCancel)(option);
62
85
  if (option === 'no') {
63
86
  throw new template_1.UserCancelledError();
64
87
  }
@@ -97,21 +120,11 @@ class ProjectBuilder {
97
120
  async execute(_context) {
98
121
  const shouldCheckEmpty = config.checkEmpty ?? true;
99
122
  if (shouldCheckEmpty && !(0, common_1.isEmptyDir)(config.targetDir)) {
100
- let shouldContinue = false;
101
- try {
102
- const answer = await inquirer_1.default.prompt([
103
- {
104
- type: 'confirm',
105
- name: 'cont',
106
- message: `Target directory ${config.targetDir} is not empty. Continue?`,
107
- default: false,
108
- },
109
- ]);
110
- shouldContinue = (0, template_1.checkCancel)(answer.cont);
111
- }
112
- catch (error) {
113
- throw new template_1.UserCancelledError(error.message);
114
- }
123
+ const shouldContinue = await p.confirm({
124
+ message: `Target directory ${config.targetDir} is not empty. Continue?`,
125
+ initialValue: false,
126
+ });
127
+ (0, template_1.checkCancel)(shouldContinue);
115
128
  if (!shouldContinue) {
116
129
  throw new template_1.UserCancelledError();
117
130
  }
@@ -133,21 +146,11 @@ class ProjectBuilder {
133
146
  description: 'Ensure target directory is empty before scaffolding',
134
147
  async execute() {
135
148
  if (node_fs_1.default.existsSync(targetDir) && !(0, common_1.isEmptyDir)(targetDir)) {
136
- let shouldContinue = false;
137
- try {
138
- const answer = await inquirer_1.default.prompt([
139
- {
140
- type: 'confirm',
141
- name: 'cont',
142
- message: `Target directory ${targetDir} is not empty. Continue?`,
143
- default: false,
144
- },
145
- ]);
146
- shouldContinue = (0, template_1.checkCancel)(answer.cont);
147
- }
148
- catch (error) {
149
- throw new template_1.UserCancelledError(error.message);
150
- }
149
+ const shouldContinue = await p.confirm({
150
+ message: `Target directory ${targetDir} is not empty. Continue?`,
151
+ initialValue: false,
152
+ });
153
+ (0, template_1.checkCancel)(shouldContinue);
151
154
  if (!shouldContinue) {
152
155
  throw new template_1.UserCancelledError();
153
156
  }
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
@@ -12,7 +45,7 @@ exports.updatePackageJson = updatePackageJson;
12
45
  // Copyright (c) 2025 TikTok Pte. Ltd.
13
46
  // Licensed under the Apache License Version 2.0 that can be found in the
14
47
  // LICENSE file in the root directory of this source tree.
15
- const inquirer_1 = __importDefault(require("inquirer"));
48
+ const p = __importStar(require("@clack/prompts"));
16
49
  const deepmerge_1 = __importDefault(require("deepmerge"));
17
50
  const node_fs_1 = __importDefault(require("node:fs"));
18
51
  const node_path_1 = __importDefault(require("node:path"));
@@ -26,6 +59,9 @@ class UserCancelledError extends Error {
26
59
  }
27
60
  exports.UserCancelledError = UserCancelledError;
28
61
  function checkCancel(value) {
62
+ if (p.isCancel(value)) {
63
+ throw new UserCancelledError();
64
+ }
29
65
  return value;
30
66
  }
31
67
  function formatProjectName(input) {
@@ -98,29 +134,19 @@ function mergePackageJson(targetPackage, extraPackage) {
98
134
  }
99
135
  async function copyTemplateWithVariables({ checkEmpty = true, from, isMergePackageJson, override = false, packageName, renameFiles = { gitignore: '.gitignore' }, skipFiles = [], to, variables = {}, version, }) {
100
136
  if (checkEmpty && !override && node_fs_1.default.existsSync(to) && !(0, common_1.isEmptyDir)(to)) {
101
- let option;
102
- try {
103
- const answer = await inquirer_1.default.prompt([
104
- {
105
- type: 'list',
106
- name: 'choice',
107
- message: `"${node_path_1.default.basename(to)}" is not empty, please choose:`,
108
- choices: [
109
- { name: 'Continue and override files', value: 'yes' },
110
- { name: 'Cancel operation', value: 'no' },
111
- ],
112
- },
113
- ]);
114
- option = checkCancel(answer.choice);
115
- }
116
- catch (error) {
117
- throw new UserCancelledError(error.message);
118
- }
137
+ const option = await p.select({
138
+ message: `"${node_path_1.default.basename(to)}" is not empty, please choose:`,
139
+ options: [
140
+ { label: 'Continue and override files', value: 'yes' },
141
+ { label: 'Cancel operation', value: 'no' },
142
+ ],
143
+ });
144
+ checkCancel(option);
119
145
  if (option === 'no') {
120
146
  throw new UserCancelledError();
121
147
  }
122
148
  }
123
- const allSkipFiles = new Set(['dist', 'node_modules', ...skipFiles]);
149
+ const allSkipFiles = new Set(['dist', 'node_modules', 'Pods', '.gradle', ...skipFiles]);
124
150
  node_fs_1.default.mkdirSync(to, { recursive: true });
125
151
  for (const file of node_fs_1.default.readdirSync(from)) {
126
152
  if (allSkipFiles.has(file)) {
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.installDependencies = installDependencies;
4
37
  exports.initializeGitRepo = initializeGitRepo;
@@ -8,6 +41,7 @@ exports.showCompletionNotes = showCompletionNotes;
8
41
  // Licensed under the Apache License Version 2.0 that can be found in the
9
42
  // LICENSE file in the root directory of this source tree.
10
43
  const node_child_process_1 = require("node:child_process");
44
+ const p = __importStar(require("@clack/prompts"));
11
45
  const ui_1 = require("../ui");
12
46
  const spinner_1 = require("../utils/spinner");
13
47
  const verbose_1 = require("../utils/verbose");
@@ -48,7 +82,7 @@ async function installDependencies(distFolder, packageManager) {
48
82
  if ((0, verbose_1.isVerboseEnabled)()) {
49
83
  (0, verbose_1.verboseLog)(`Install failed: ${error instanceof Error ? error.message : String(error)}`);
50
84
  }
51
- console.warn(ui_1.ui.warn(`Warning: Failed to install dependencies. Run '${packageManager} install' manually.`));
85
+ p.log.warn(`Failed to install dependencies. Run '${packageManager} install' manually.`);
52
86
  return false;
53
87
  }
54
88
  }
@@ -60,7 +94,7 @@ async function initializeGitRepo(distFolder) {
60
94
  (0, node_child_process_1.execSync)('git --version', { stdio: stdioMode });
61
95
  }
62
96
  catch {
63
- console.warn(ui_1.ui.warn('Warning: Git is not installed or not in PATH. Skipping git initialization.'));
97
+ p.log.warn('Git is not installed or not in PATH. Skipping git initialization.');
64
98
  return;
65
99
  }
66
100
  s.start('Initializing git repository...');
@@ -72,7 +106,7 @@ async function initializeGitRepo(distFolder) {
72
106
  }
73
107
  catch (error) {
74
108
  s.stop('Failed to initialize git repository');
75
- console.warn(ui_1.ui.warn('Warning: Failed to initialize git repository. Run "git init" manually.'));
109
+ p.log.warn('Failed to initialize git repository. Run "git init" manually.');
76
110
  }
77
111
  }
78
112
  function detectPackageManager() {
@@ -120,7 +154,6 @@ function detectPackageManager() {
120
154
  }
121
155
  }
122
156
  function showCompletionNotes(targetDir, packageManager, didInstall = false) {
123
- console.log(ui_1.ui.success(`✔ Project created at ${targetDir}`));
124
157
  const formatScriptCommand = (script) => {
125
158
  const pm = packageManager ?? 'npm';
126
159
  return pm === 'npm' ? `${pm} run ${script}` : `${pm} ${script}`;
@@ -134,14 +167,9 @@ function showCompletionNotes(targetDir, packageManager, didInstall = false) {
134
167
  }
135
168
  nextSteps.push(formatScriptCommand('run:ios'));
136
169
  nextSteps.push(formatScriptCommand('run:android'));
137
- console.log(ui_1.ui.headline('Next steps'));
138
- nextSteps.forEach(step => console.log(ui_1.ui.headline(step)));
139
170
  const tips = [
140
171
  'iOS: ensure Xcode Command Line Tools are installed.',
141
172
  'Android: ensure ANDROID_HOME and SDK platforms are set.',
142
173
  ];
143
- tips.forEach(tip => {
144
- console.log(ui_1.ui.tip(tip));
145
- });
146
- console.log(ui_1.ui.success('Successfully created app project!'));
174
+ p.note([...nextSteps, '', ...tips.map(t => ui_1.ui.tip(t))].join('\n'), `✔ Project created at ${targetDir}`);
147
175
  }
@@ -1,7 +1,37 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
5
35
  Object.defineProperty(exports, "__esModule", { value: true });
6
36
  exports.askProjectName = askProjectName;
7
37
  exports.askTemplate = askTemplate;
@@ -16,43 +46,23 @@ exports.confirmRemoveExistingDir = confirmRemoveExistingDir;
16
46
  // Copyright (c) 2025 TikTok Pte. Ltd.
17
47
  // Licensed under the Apache License Version 2.0 that can be found in the
18
48
  // LICENSE file in the root directory of this source tree.
19
- const inquirer_1 = __importDefault(require("inquirer"));
49
+ const p = __importStar(require("@clack/prompts"));
20
50
  const template_1 = require("../core/project-builder/template");
21
- const ui_1 = require("../ui");
22
51
  const constants_1 = require("./constants");
23
52
  const android_dsl_1 = require("./android-dsl");
24
53
  const namespace_1 = require("./namespace");
25
- function handlePromptError(error) {
26
- if (error instanceof Error) {
27
- throw new template_1.UserCancelledError(error.message);
28
- }
29
- throw new template_1.UserCancelledError();
30
- }
31
54
  async function askProjectName(flags, defaultName = constants_1.DEFAULT_PROJECT_NAME) {
32
55
  if (!flags.yes) {
33
- try {
34
- const { projectName } = await inquirer_1.default.prompt([
35
- {
36
- type: 'input',
37
- name: 'projectName',
38
- message: ui_1.ui.prompt('Project name'),
39
- default: defaultName,
40
- transformer(value) {
41
- const next = value && value.length > 0 ? value : defaultName;
42
- return next;
43
- },
44
- validate(value) {
45
- if (!value || value.length === 0)
46
- return 'Project name is required';
47
- return true;
48
- },
49
- },
50
- ]);
51
- return (0, template_1.checkCancel)(projectName);
52
- }
53
- catch (error) {
54
- handlePromptError(error);
55
- }
56
+ const projectName = await p.text({
57
+ message: 'Project name',
58
+ defaultValue: defaultName,
59
+ placeholder: defaultName,
60
+ validate(value) {
61
+ if (!value || value.length === 0)
62
+ return 'Project name is required';
63
+ },
64
+ });
65
+ return (0, template_1.checkCancel)(projectName);
56
66
  }
57
67
  return defaultName;
58
68
  }
@@ -60,23 +70,14 @@ async function askTemplate(flags, initial) {
60
70
  if (initial)
61
71
  return initial;
62
72
  if (!flags.yes) {
63
- try {
64
- const { template } = await inquirer_1.default.prompt([
65
- {
66
- type: 'list',
67
- name: 'template',
68
- message: ui_1.ui.prompt('Choose a template'),
69
- choices: [
70
- { value: constants_1.DEFAULT_TEMPLATE_NAME, name: 'sparkling-default (Official Sparkling starter project)' },
71
- { value: constants_1.CUSTOM_TEMPLATE_OPTION, name: 'Custom template (Local path, Git URL, or npm package)' },
72
- ],
73
- },
74
- ]);
75
- return (0, template_1.checkCancel)(template);
76
- }
77
- catch (error) {
78
- handlePromptError(error);
79
- }
73
+ const template = await p.select({
74
+ message: 'Choose a template',
75
+ options: [
76
+ { value: constants_1.DEFAULT_TEMPLATE_NAME, label: 'sparkling-default (Official Sparkling starter project)' },
77
+ { value: constants_1.CUSTOM_TEMPLATE_OPTION, label: 'Custom template (Local path, Git URL, or npm package)' },
78
+ ],
79
+ });
80
+ return (0, template_1.checkCancel)(template);
80
81
  }
81
82
  return constants_1.DEFAULT_TEMPLATE_NAME;
82
83
  }
@@ -84,87 +85,51 @@ async function askCustomTemplatePath(flags) {
84
85
  if (flags.yes) {
85
86
  throw new Error('Custom template requires interactive input or a direct --template path.');
86
87
  }
87
- try {
88
- const { templatePath } = await inquirer_1.default.prompt([
89
- {
90
- type: 'input',
91
- name: 'templatePath',
92
- message: ui_1.ui.prompt('Enter custom template path, GitHub URL, or npm package'),
93
- default: '',
94
- validate(value) {
95
- if (!value || value.length === 0)
96
- return 'Template path is required';
97
- return true;
98
- },
99
- },
100
- ]);
101
- return (0, template_1.checkCancel)(templatePath);
102
- }
103
- catch (error) {
104
- handlePromptError(error);
105
- }
88
+ const templatePath = await p.text({
89
+ message: 'Enter custom template path, GitHub URL, or npm package',
90
+ validate(value) {
91
+ if (!value || value.length === 0)
92
+ return 'Template path is required';
93
+ },
94
+ });
95
+ return (0, template_1.checkCancel)(templatePath);
106
96
  }
107
97
  async function askAndroidDsl(flags) {
108
98
  if (!flags.yes) {
109
- try {
110
- const { androidDsl } = await inquirer_1.default.prompt([
111
- {
112
- type: 'list',
113
- name: 'androidDsl',
114
- message: ui_1.ui.prompt('Android Gradle build files'),
115
- choices: [
116
- { value: 'kts', name: 'Kotlin DSL (.gradle.kts)' },
117
- { value: 'groovy', name: 'Groovy (.gradle)' },
118
- ],
119
- },
120
- ]);
121
- return (0, template_1.checkCancel)(androidDsl) === 'groovy' ? 'groovy' : 'kts';
122
- }
123
- catch (error) {
124
- handlePromptError(error);
125
- }
99
+ const androidDsl = await p.select({
100
+ message: 'Android Gradle build files',
101
+ options: [
102
+ { value: 'kts', label: 'Kotlin DSL (.gradle.kts)' },
103
+ { value: 'groovy', label: 'Groovy (.gradle)' },
104
+ ],
105
+ });
106
+ return (0, template_1.checkCancel)(androidDsl) === 'groovy' ? 'groovy' : 'kts';
126
107
  }
127
108
  return android_dsl_1.DEFAULT_ANDROID_DSL;
128
109
  }
129
110
  async function askDevTools(flags) {
130
111
  if (!flags.yes) {
131
- try {
132
- const { devTools } = await inquirer_1.default.prompt([
133
- {
134
- type: 'checkbox',
135
- name: 'devTools',
136
- message: ui_1.ui.prompt('Select development tools'),
137
- choices: [{ value: 'testing', name: 'Add ReactLynx Testing Library for unit testing' }],
138
- },
139
- ]);
140
- return (0, template_1.checkCancel)(devTools);
141
- }
142
- catch (error) {
143
- handlePromptError(error);
144
- }
112
+ const devTools = await p.multiselect({
113
+ message: 'Select development tools',
114
+ required: false,
115
+ options: [{ value: 'testing', label: 'Add ReactLynx Testing Library for unit testing' }],
116
+ });
117
+ return (0, template_1.checkCancel)(devTools);
145
118
  }
146
119
  return [];
147
120
  }
148
121
  async function askAdditionalTools(flags) {
149
122
  if (!flags.yes) {
150
- try {
151
- const { tools } = await inquirer_1.default.prompt([
152
- {
153
- type: 'checkbox',
154
- name: 'tools',
155
- message: ui_1.ui.prompt('Select optional tooling'),
156
- choices: [
157
- { value: 'eslint', name: 'ESLint (Standard linting configuration)' },
158
- { value: 'prettier', name: 'Prettier (Auto-formatting defaults)' },
159
- { value: 'biome', name: 'Biome (Biome + Biome formatter)' },
160
- ],
161
- },
162
- ]);
163
- return (0, template_1.checkCancel)(tools);
164
- }
165
- catch (error) {
166
- handlePromptError(error);
167
- }
123
+ const tools = await p.multiselect({
124
+ message: 'Select optional tooling',
125
+ required: false,
126
+ options: [
127
+ { value: 'eslint', label: 'ESLint (Standard linting configuration)' },
128
+ { value: 'prettier', label: 'Prettier (Auto-formatting defaults)' },
129
+ { value: 'biome', label: 'Biome (Biome + Biome formatter)' },
130
+ ],
131
+ });
132
+ return (0, template_1.checkCancel)(tools);
168
133
  }
169
134
  return [];
170
135
  }
@@ -173,25 +138,16 @@ async function askNamespace(defaultNamespace, flags) {
173
138
  if (provided)
174
139
  return provided;
175
140
  if (!flags.yes) {
176
- try {
177
- const { namespace } = await inquirer_1.default.prompt([
178
- {
179
- type: 'input',
180
- name: 'namespace',
181
- message: ui_1.ui.prompt('Package namespace (Android package / iOS bundle id)'),
182
- default: defaultNamespace,
183
- validate(value) {
184
- if (!(0, namespace_1.isValidNamespace)(value))
185
- return 'Use reverse-DNS format, e.g. com.example.app';
186
- return true;
187
- },
188
- },
189
- ]);
190
- return (0, template_1.checkCancel)(namespace);
191
- }
192
- catch (error) {
193
- handlePromptError(error);
194
- }
141
+ const namespace = await p.text({
142
+ message: 'Package namespace (Android package / iOS bundle id)',
143
+ defaultValue: defaultNamespace,
144
+ placeholder: defaultNamespace,
145
+ validate(value) {
146
+ if (!(0, namespace_1.isValidNamespace)(value))
147
+ return 'Use reverse-DNS format, e.g. com.example.app';
148
+ },
149
+ });
150
+ return (0, template_1.checkCancel)(namespace);
195
151
  }
196
152
  return defaultNamespace;
197
153
  }
@@ -199,20 +155,11 @@ async function confirmInstall(packageManager, flags) {
199
155
  if (flags.install !== undefined)
200
156
  return flags.install !== false;
201
157
  if (!flags.yes) {
202
- try {
203
- const { installNow } = await inquirer_1.default.prompt([
204
- {
205
- type: 'confirm',
206
- name: 'installNow',
207
- message: ui_1.ui.prompt(`Install JS dependencies now with ${packageManager}?`),
208
- default: true,
209
- },
210
- ]);
211
- return (0, template_1.checkCancel)(installNow);
212
- }
213
- catch (error) {
214
- handlePromptError(error);
215
- }
158
+ const installNow = await p.confirm({
159
+ message: `Install JS dependencies now with ${packageManager}?`,
160
+ initialValue: true,
161
+ });
162
+ return (0, template_1.checkCancel)(installNow);
216
163
  }
217
164
  return true;
218
165
  }
@@ -220,38 +167,20 @@ async function confirmInitGit(flags) {
220
167
  if (flags.git !== undefined)
221
168
  return flags.git !== false;
222
169
  if (!flags.yes) {
223
- try {
224
- const { initGit } = await inquirer_1.default.prompt([
225
- {
226
- type: 'confirm',
227
- name: 'initGit',
228
- message: ui_1.ui.prompt('Initialize a git repository?'),
229
- default: true,
230
- },
231
- ]);
232
- return (0, template_1.checkCancel)(initGit);
233
- }
234
- catch (error) {
235
- handlePromptError(error);
236
- }
170
+ const initGit = await p.confirm({
171
+ message: 'Initialize a git repository?',
172
+ initialValue: true,
173
+ });
174
+ return (0, template_1.checkCancel)(initGit);
237
175
  }
238
176
  return true;
239
177
  }
240
178
  async function confirmRemoveExistingDir(targetDir, flags) {
241
179
  if (flags.yes)
242
180
  return true;
243
- try {
244
- const { removeDir } = await inquirer_1.default.prompt([
245
- {
246
- type: 'confirm',
247
- name: 'removeDir',
248
- message: ui_1.ui.prompt(`Target directory "${targetDir}" exists and is not empty. Remove existing files?`),
249
- default: true,
250
- },
251
- ]);
252
- return (0, template_1.checkCancel)(removeDir);
253
- }
254
- catch (error) {
255
- handlePromptError(error);
256
- }
181
+ const removeDir = await p.confirm({
182
+ message: `Target directory "${targetDir}" exists and is not empty. Remove existing files?`,
183
+ initialValue: true,
184
+ });
185
+ return (0, template_1.checkCancel)(removeDir);
257
186
  }
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
@@ -8,6 +41,7 @@ exports.createSparklingApp = createSparklingApp;
8
41
  // Copyright (c) 2025 TikTok Pte. Ltd.
9
42
  // Licensed under the Apache License Version 2.0 that can be found in the
10
43
  // LICENSE file in the root directory of this source tree.
44
+ const p = __importStar(require("@clack/prompts"));
11
45
  const fs_1 = __importDefault(require("fs"));
12
46
  const path_1 = __importDefault(require("path"));
13
47
  const action_runner_1 = require("./core/actions/action-runner");
@@ -64,11 +98,7 @@ async function createSparklingApp(options) {
64
98
  const versionLabel = version
65
99
  ? `${version}${isWorkspaceBuild ? "-local" : ""}`
66
100
  : "0.0.0";
67
- console.log(ui_1.ui.headline(`create-sparkling-app v${versionLabel}`));
68
- console.log(ui_1.ui.headline("┌──────────────────────────────────────────────────────────────┐"));
69
- console.log(ui_1.ui.headline("│ Welcome to Sparkling! │"));
70
- console.log(ui_1.ui.headline("│ Create a Lynx app with various native abilities in minutes. │"));
71
- console.log(ui_1.ui.headline("└──────────────────────────────────────────────────────────────┘"));
101
+ p.intro(ui_1.ui.headline(`create-sparkling-app v${versionLabel}`));
72
102
  const projectName = args.name ?? (await (0, user_prompts_1.askProjectName)(flags));
73
103
  const formatted = (0, template_1.formatProjectName)(projectName);
74
104
  const { packageName, targetDir } = formatted;
@@ -232,8 +262,8 @@ async function createSparklingApp(options) {
232
262
  didInstall = await (0, post_create_1.installDependencies)(distFolder, packageManager);
233
263
  }
234
264
  if (shouldInitGit) {
235
- console.log(ui_1.ui.info('Setting up git repository, this may take a moment...'));
236
265
  await (0, post_create_1.initializeGitRepo)(distFolder);
237
266
  }
238
267
  (0, post_create_1.showCompletionNotes)(targetDir, packageManager, didInstall);
268
+ p.outro(ui_1.ui.success('Happy hacking!'));
239
269
  }
package/dist/index.js CHANGED
@@ -2,12 +2,46 @@
2
2
  // Copyright (c) 2025 TikTok Pte. Ltd.
3
3
  // Licensed under the Apache License Version 2.0 that can be found in the
4
4
  // LICENSE file in the root directory of this source tree.
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
5
38
  var __importDefault = (this && this.__importDefault) || function (mod) {
6
39
  return (mod && mod.__esModule) ? mod : { "default": mod };
7
40
  };
8
41
  Object.defineProperty(exports, "__esModule", { value: true });
9
42
  exports.createSparklingApp = void 0;
10
43
  exports.main = main;
44
+ const p = __importStar(require("@clack/prompts"));
11
45
  const help_1 = __importDefault(require("./help"));
12
46
  const init_1 = __importDefault(require("./init"));
13
47
  const template_1 = require("./core/project-builder/template");
@@ -64,9 +98,11 @@ async function main() {
64
98
  if (require.main === module || process.env.NODE_ENV !== 'test') {
65
99
  main().catch(error => {
66
100
  if (error instanceof template_1.UserCancelledError) {
101
+ p.cancel('Operation cancelled.');
67
102
  process.exit(0);
68
103
  }
69
- console.error('An error occurred:', error);
104
+ p.cancel('An error occurred.');
105
+ console.error(error);
70
106
  if ((0, verbose_1.isVerboseEnabled)() && error instanceof Error && error.stack) {
71
107
  (0, verbose_1.verboseLog)(error.stack);
72
108
  }
@@ -1,30 +1,78 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.createSpinner = createSpinner;
4
37
  // Copyright (c) 2025 TikTok Pte. Ltd.
5
38
  // Licensed under the Apache License Version 2.0 that can be found in the
6
39
  // LICENSE file in the root directory of this source tree.
7
- const ui_1 = require("../ui");
40
+ const p = __importStar(require("@clack/prompts"));
8
41
  function createSpinner() {
9
- let activeMessage = null;
42
+ // p.spinner() uses animated cursor output that requires a TTY.
43
+ // Fall back to simple console.log in non-TTY environments (CI, piped output).
44
+ if (!process.stdout.isTTY) {
45
+ let activeMessage = null;
46
+ return {
47
+ message(message) {
48
+ if (message)
49
+ console.log(message);
50
+ },
51
+ start(message) {
52
+ activeMessage = message ?? null;
53
+ if (message)
54
+ console.log(message);
55
+ },
56
+ stop(message, _code) {
57
+ const finalMessage = message ?? activeMessage;
58
+ if (finalMessage)
59
+ console.log(finalMessage);
60
+ activeMessage = null;
61
+ },
62
+ };
63
+ }
64
+ const spin = p.spinner();
10
65
  return {
11
66
  message(message) {
12
67
  if (message) {
13
- console.log(ui_1.ui.info(message));
68
+ spin.message(message);
14
69
  }
15
70
  },
16
71
  start(message) {
17
- activeMessage = message ?? null;
18
- if (message) {
19
- console.log(ui_1.ui.info(message));
20
- }
72
+ spin.start(message ?? '');
21
73
  },
22
74
  stop(message, _code) {
23
- const finalMessage = message ?? activeMessage;
24
- if (finalMessage) {
25
- console.log(ui_1.ui.info(finalMessage));
26
- }
27
- activeMessage = null;
75
+ spin.stop(message ?? '');
28
76
  },
29
77
  };
30
78
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-sparkling-app",
3
- "version": "2.0.0",
3
+ "version": "2.1.0-rc.0",
4
4
  "homepage": "https://tiktok.github.io/sparkling/",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,7 +21,6 @@
21
21
  "description": "CLI tool for creating Sparkling app projects",
22
22
  "devDependencies": {
23
23
  "@types/jest": "^29.5.12",
24
- "@types/inquirer": "^9.0.8",
25
24
  "@types/node": "^22.15.0",
26
25
  "jest": "^29.7.0",
27
26
  "ts-jest": "^29.1.2",
@@ -31,7 +30,7 @@
31
30
  "chalk": "^4.1.2",
32
31
  "commander": "^12.1.0",
33
32
  "deepmerge": "^4.3.1",
34
- "inquirer": "^8.2.6"
33
+ "@clack/prompts": "^0.10.1"
35
34
  },
36
35
  "scripts": {
37
36
  "test": "jest",