zmp-cli 3.10.0-rc.0 → 3.11.0-rc.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.
package/.eslintignore CHANGED
@@ -1,7 +1,8 @@
1
1
  create/templates/react/pages
2
2
  create/templates/vue/pages
3
3
  create/templates/common
4
+ create/templates/zaui
4
5
  ui/src
5
6
  ui/www
6
7
  node_modules
7
- example
8
+ example
package/config/index.js CHANGED
@@ -25,6 +25,7 @@ module.exports = {
25
25
  env: {
26
26
  appId: 'APP_ID',
27
27
  token: 'ZMP_TOKEN',
28
+ accessToken: 'VITE_ACCESS_TOKEN',
28
29
  },
29
30
  error_code: {
30
31
  app_config_not_found: -1400,
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env node
2
+ /* eslint no-console: off */
3
+ const exec = require('exec-sh');
4
+ const path = require('path');
5
+ const chalk = require('chalk');
6
+ const logSymbols = require('log-symbols');
7
+ const fse = require('../utils/fs-extra');
8
+ const generatePackageJson = require('./utils/generate-package-json');
9
+ const generateNpmScripts = require('./utils/generate-npm-scripts');
10
+
11
+ const generateAppConfig = require('./utils/generate-app-config');
12
+
13
+ const createFolders = require('./templates/create-folders');
14
+ const copyAssets = require('./templates/copy-assets');
15
+ const generateReadme = require('./utils/generate-readme');
16
+ const generateGitignore = require('./utils/generate-gitignore');
17
+ const log = require('../utils/log');
18
+ const config = require('../config');
19
+
20
+ const waitText = chalk.gray('(Please wait, it can take a while)');
21
+
22
+ module.exports = async (
23
+ options = {},
24
+ logger,
25
+ { exitOnError = true, iconFile = null } = {}
26
+ ) => {
27
+ const cwd = options.cwd || process.cwd();
28
+ const isRunningInCwd = cwd === process.cwd();
29
+ function errorExit(err) {
30
+ log.error(err.stderr || err);
31
+ if (exitOnError) process.exit(1);
32
+ }
33
+ if (!logger) {
34
+ // eslint-disable-next-line
35
+ logger = {
36
+ statusStart() {},
37
+ statusDone() {},
38
+ statusError() {},
39
+ text() {},
40
+ error() {},
41
+ };
42
+ }
43
+ // App config
44
+ logger.statusStart('Generating app-config.json');
45
+ const appConfig = generateAppConfig(options);
46
+
47
+ fse.writeFileSync(
48
+ path.join(cwd, config.filename.appConfig),
49
+ appConfig.content
50
+ );
51
+ logger.statusDone('Generating app-config.json');
52
+
53
+ if (!options.newProject) {
54
+ const deployScripts = generateNpmScripts(['r']).map((s) => {
55
+ return `${s.icon} Run "npm run ${s.name}" - ${s.description}`;
56
+ });
57
+ logger.text(deployScripts.join('\n'));
58
+ process.exit(0);
59
+ }
60
+
61
+ // Package
62
+ logger.statusStart('Generating package.json');
63
+ const packageJson = generatePackageJson(options);
64
+ fse.writeFileSync(path.join(cwd, 'package.json'), packageJson.content);
65
+ fse.writeFileSync(
66
+ path.join(cwd, config.filename.zmpConfig),
67
+ JSON.stringify(options, '', 2)
68
+ );
69
+
70
+ logger.statusDone('Generating package.json');
71
+
72
+ // Create Folders
73
+ logger.statusStart('Creating required folders structure');
74
+ try {
75
+ createFolders(options);
76
+ } catch (err) {
77
+ logger.statusError('Error creating required folders structure');
78
+ // if (err) logger.error(err.stderr);
79
+ errorExit(err);
80
+ }
81
+ logger.statusDone('Creating required folders structure');
82
+
83
+ // Install NPM depenencies
84
+ logger.statusStart(`${'Installing NPM Dependencies'} ${waitText}`);
85
+ try {
86
+ if (!isRunningInCwd) {
87
+ await exec.promise(
88
+ `cd ${cwd.replace(
89
+ / /g,
90
+ '\\ '
91
+ )} && npm install ${packageJson.dependencies.join(' ')} --save`,
92
+ true
93
+ );
94
+ await exec.promise(
95
+ `cd ${cwd.replace(/ /g, '\\ ')} && npm install --save`,
96
+ true
97
+ );
98
+ } else {
99
+ await exec.promise(
100
+ `npm install ${packageJson.dependencies.join(' ')} --save`,
101
+ true
102
+ );
103
+ await exec.promise(`npm install --save`, true);
104
+ }
105
+ } catch (err) {
106
+ logger.statusError('Error installing NPM Dependencies');
107
+ // if (err) logger.error(err.stderr);
108
+ errorExit(err);
109
+ return;
110
+ }
111
+ logger.statusDone('Installing NPM Dependencies');
112
+ // Install NPM dev depenencies
113
+ logger.statusStart(`${'Installing NPM Dev Dependencies'} ${waitText}`);
114
+ try {
115
+ if (!isRunningInCwd) {
116
+ await exec.promise(
117
+ `cd ${cwd.replace(
118
+ / /g,
119
+ '\\ '
120
+ )} && npm install ${packageJson.devDependencies.join(' ')} --save-dev`,
121
+ true
122
+ );
123
+ } else {
124
+ await exec.promise(
125
+ `npm install ${packageJson.devDependencies.join(' ')} --save-dev`,
126
+ true
127
+ );
128
+ }
129
+ } catch (err) {
130
+ logger.statusError('Error installing NPM Dev Dependencies');
131
+ // if (err) logger.error(err.stderr);
132
+ errorExit(err);
133
+ return;
134
+ }
135
+ logger.statusDone('Installing NPM Dev Dependencies');
136
+
137
+ if (packageJson.postInstall && packageJson.postInstall.length) {
138
+ logger.statusStart('Executing NPM Scripts');
139
+ try {
140
+ if (!isRunningInCwd) {
141
+ await exec.promise(
142
+ `cd ${cwd.replace(/ /g, '\\ ')} && npm run postinstall`,
143
+ true
144
+ );
145
+ } else {
146
+ await exec.promise('npm run postinstall', true);
147
+ }
148
+ } catch (err) {
149
+ logger.statusError('Error executing NPM Scripts');
150
+ // if (err) logger.error(err.stderr);
151
+ errorExit(err);
152
+ return;
153
+ }
154
+ logger.statusDone('Executing NPM Scripts');
155
+ }
156
+
157
+ // Create Project Files
158
+ logger.statusStart('Creating project files');
159
+ const filesToCopy = copyAssets(options, iconFile);
160
+ try {
161
+ // eslint-disable-next-line
162
+ await Promise.all(
163
+ filesToCopy.map((f) => {
164
+ if (f.from) {
165
+ return fse.copyFileAsync(f.from, f.to);
166
+ }
167
+ if (f.content) {
168
+ return fse.writeFileAsync(f.to, f.content);
169
+ }
170
+ return Promise.resolve();
171
+ })
172
+ );
173
+ } catch (err) {
174
+ logger.statusError('Error creating project files');
175
+ // if (err) logger.error(err.stderr || err);
176
+ errorExit(err);
177
+ return;
178
+ }
179
+
180
+ // Generate Readme
181
+ const readMeContent = generateReadme(options);
182
+ try {
183
+ fse.writeFileSync(path.join(cwd, 'README.md'), readMeContent);
184
+ } catch (err) {
185
+ logger.statusError('Error creating project files');
186
+ // if (err) logger.error(err.stderr || err);
187
+ errorExit(err);
188
+ return;
189
+ }
190
+
191
+ // Generate .gitignore
192
+ const gitignoreContent = generateGitignore(options);
193
+ try {
194
+ fse.writeFileSync(path.join(cwd, '.gitignore'), gitignoreContent);
195
+ } catch (err) {
196
+ logger.statusError('Error creating project files');
197
+ // if (err) logger.error(err.stderr || err);
198
+ errorExit(err);
199
+ return;
200
+ }
201
+
202
+ logger.statusDone('Creating project files');
203
+
204
+ let npmScripts = generateNpmScripts(['s', 'r']).map((s) => {
205
+ return `- ${s.icon} Run "npm run ${s.name}" - ${s.description}`;
206
+ });
207
+
208
+ // Final Text
209
+ const finalText = `
210
+ ${chalk.bold(logSymbols.success)} ${chalk.bold('Done!')} 💪
211
+
212
+ ${chalk.bold(logSymbols.info)} ${chalk.bold('Next steps:')}
213
+ ${npmScripts.join('\n ')}
214
+ - 📖 Visit documentation at ${chalk.bold('https://zmp.io/docs/')}
215
+ - 📖 Check ${chalk.bold(
216
+ 'README.md'
217
+ )} in project root folder with further instructions
218
+ `;
219
+
220
+ logger.text(finalText);
221
+ };
@@ -13,11 +13,17 @@ const generateRoutes = require('./generate-routes');
13
13
  const generateManifest = require('./generate-manifest');
14
14
  const generateServiceWorker = require('./generate-service-worker');
15
15
  const generateZMPCustom = require('./generate-zmp-custom');
16
-
16
+ const copyZauiAssets = require('./zaui/copy-assets');
17
17
  module.exports = (options, iconFile) => {
18
18
  const cwd = options.cwd || process.cwd();
19
- const { framework, theming, cssPreProcessor, customBuild, includeTailwind } =
20
- options;
19
+ const {
20
+ zaui,
21
+ framework,
22
+ theming,
23
+ cssPreProcessor,
24
+ customBuild,
25
+ includeTailwind,
26
+ } = options;
21
27
 
22
28
  const srcFolder = 'src';
23
29
 
@@ -29,6 +35,9 @@ module.exports = (options, iconFile) => {
29
35
  if (framework === 'react-typescript') {
30
36
  toCopy.push(...copyReactTsAssets(options));
31
37
  }
38
+ if (zaui) {
39
+ toCopy.push(...copyZauiAssets(options));
40
+ }
32
41
  if (theming.iconFonts) {
33
42
  // Copy Icons CSS
34
43
  toCopy.push({
@@ -2,7 +2,7 @@
2
2
  const generateCoreRoot = require('./core/generate-root.js');
3
3
 
4
4
  module.exports = (options) => {
5
- const { name, framework, theming } = options;
5
+ const { name, framework, theming, zaui } = options;
6
6
  const srcFolder = '/src/';
7
7
  const iconsAssetsFolder = 'static';
8
8
 
@@ -10,7 +10,7 @@ module.exports = (options) => {
10
10
  const scripts = `
11
11
  <!-- built script files will be auto injected -->
12
12
  ${
13
- framework === 'react' || framework === 'vue'
13
+ zaui || framework === 'react' || framework === 'vue'
14
14
  ? `<script type="module" src="${srcFolder}app.js"></script>`
15
15
  : ''
16
16
  }
@@ -3,12 +3,14 @@ const generateVueScripts = require('./vue/generate-scripts');
3
3
  const generateReactScripts = require('./react/generate-scripts');
4
4
  const generateSvelteScripts = require('./svelte/generate-scripts');
5
5
  const generateReactTsScripts = require('./react-typescript/generate-scripts');
6
+ const generateZauiScripts = require('./zaui/generate-scripts');
6
7
  module.exports = (options) => {
7
- const { framework } = options;
8
+ const { framework, zaui } = options;
8
9
  if (framework === 'core') return generateCoreScripts(options);
9
10
  if (framework === 'vue') return generateVueScripts(options);
10
11
  if (framework === 'react') return generateReactScripts(options);
11
12
  if (framework === 'svelte') return generateSvelteScripts(options);
12
13
  if (framework === 'react-typescript') return generateReactTsScripts(options);
14
+ if (zaui) return generateZauiScripts(options);
13
15
  return '';
14
16
  };
@@ -2,12 +2,30 @@ const indent = require('../utils/indent');
2
2
  const { colorThemeCSSProperties } = require('../utils/colors');
3
3
 
4
4
  module.exports = (options) => {
5
- const { template, theming } = options;
5
+ const { template, theming, zaui } = options;
6
6
  const { customColor, color, fillBars } = theming;
7
7
 
8
8
  let styles = '';
9
9
 
10
+ if (zaui) {
11
+ return `
12
+ *{
13
+ box-sizing: border-box;
14
+ }
15
+ #app{
16
+ width: 100%;
17
+ height: 100%;
18
+ }
19
+ .page{
20
+ padding: 16px;
21
+ min-height: 100%;
22
+ overflow-y: auto;
23
+ }
24
+ `;
25
+ }
26
+
10
27
  let themeRgb = [0, 122, 255];
28
+
11
29
  if (customColor && color) {
12
30
  const customProps = colorThemeCSSProperties(`${color}`);
13
31
  themeRgb = customProps['--zmp-theme-color-rgb']
@@ -19,18 +37,18 @@ module.exports = (options) => {
19
37
  /* Custom color theme properties */
20
38
  :root {
21
39
  ${Object.keys(customProps)
22
- .filter(
23
- (prop) =>
24
- prop !== '--zmp-tabbar-fill-link-active-color' &&
25
- prop !== '--zmp-tabbar-fill-link-active-border-color'
26
- )
27
- .map((prop) => `${prop}: ${customProps[prop]};`)
28
- .join('\n ')}
40
+ .filter(
41
+ (prop) =>
42
+ prop !== '--zmp-tabbar-fill-link-active-color' &&
43
+ prop !== '--zmp-tabbar-fill-link-active-border-color'
44
+ )
45
+ .map((prop) => `${prop}: ${customProps[prop]};`)
46
+ .join('\n ')}
29
47
  }
30
48
  :root.theme-dark,:root .theme-dark {
31
49
  ${Object.keys(customProps)
32
- .map((prop) => `${prop}: ${customProps[prop]};`)
33
- .join('\n ')}
50
+ .map((prop) => `${prop}: ${customProps[prop]};`)
51
+ .join('\n ')}
34
52
  }
35
53
  `
36
54
  );
@@ -0,0 +1,12 @@
1
+ module.exports = {
2
+ presets: [
3
+ '@babel/preset-react',
4
+ [
5
+ '@babel/preset-env',
6
+ {
7
+ modules: false,
8
+ },
9
+ ],
10
+ ],
11
+ plugins: ['@babel/plugin-transform-runtime'],
12
+ };
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import { Avatar } from 'zaui';
3
+
4
+ const UserCard = ({ user }) => {
5
+ return (
6
+ <div style={{ display: 'flex', width: '100%' }}>
7
+ <Avatar story online src={user.avatar.startsWith('http') ? user.avatar : null}>{user.avatar}</Avatar>
8
+ <div style={{ marginLeft: 16 }}>
9
+ <div style={{ marginBottom: 0, fontWeight: 'bold', fontSize: '20px' }}>{user.name}</div>
10
+ <div>{user.id}</div>
11
+ </div>
12
+ </div>
13
+ )
14
+ };
15
+
16
+ UserCard.displayName = 'zmp-user-card'
17
+
18
+ export default UserCard;
@@ -0,0 +1,10 @@
1
+ const appId = window.APP_ID
2
+ const env = import.meta.env.MODE
3
+
4
+ const basePath = env === 'development' ? '' : `/zapps/${appId}`
5
+
6
+ export default {
7
+ appId,
8
+ env,
9
+ basePath
10
+ }
@@ -0,0 +1,75 @@
1
+ const path = require('path');
2
+ const generateHomePage = require('./generate-home-page');
3
+ const generateRoot = require('./generate-root');
4
+ const generateRecoil = require('../generate-recoil');
5
+ const copyPages = require('./pages');
6
+
7
+ module.exports = (options) => {
8
+ const cwd = options.cwd || process.cwd();
9
+ const { template, stateManagement } = options;
10
+ const toCopy = [];
11
+
12
+ // Copy Pages
13
+
14
+ let pages = [];
15
+ if (template !== 'blank')
16
+ pages.push(
17
+ ...[
18
+ { fileName: 'about', content: 'copyAbout' },
19
+ { fileName: 'user', content: 'copyUser' },
20
+ { fileName: 'form', content: 'copyForm' },
21
+ ]
22
+ );
23
+
24
+
25
+ pages.forEach(({ fileName, content }) => {
26
+ const dest = path.resolve(cwd, 'src', 'pages', `${fileName}.jsx`);
27
+ toCopy.push({
28
+ content: copyPages[content](options),
29
+ to: dest,
30
+ });
31
+ });
32
+
33
+
34
+ toCopy.push({
35
+ content: generateHomePage(options),
36
+ to: path.resolve(cwd, 'src', 'pages', 'index.jsx'),
37
+ });
38
+
39
+ // Copy compoents
40
+ const components = [
41
+ ...(template !== 'blank' ? ['user-card'] : []),
42
+ ];
43
+ components.forEach((name) => {
44
+ const src = path.resolve(__dirname, 'components', `${name}.jsx`);
45
+ const dest = path.resolve(cwd, 'src', 'components', `${name}.jsx`);
46
+ toCopy.push({
47
+ from: src,
48
+ to: dest,
49
+ });
50
+ });
51
+
52
+ toCopy.push({
53
+ content: generateRoot(options),
54
+ to: path.resolve(cwd, 'src', 'components', 'app.jsx'),
55
+ });
56
+
57
+ if (stateManagement === 'recoil') {
58
+ toCopy.push({
59
+ content: generateRecoil(options),
60
+ to: path.resolve(cwd, 'src', 'state.js'),
61
+ });
62
+ }
63
+
64
+ toCopy.push({
65
+ from: path.resolve(__dirname, 'vite.config.js'),
66
+ to: path.resolve(cwd, 'vite.config.js'),
67
+ });
68
+
69
+ toCopy.push({
70
+ from: path.resolve(__dirname, 'config','index.js'),
71
+ to: path.resolve(cwd, 'src/config','index.js'),
72
+ });
73
+
74
+ return toCopy;
75
+ };
@@ -0,0 +1,43 @@
1
+ const indent = require('../../utils/indent');
2
+ module.exports = (options) => {
3
+ const { name, template, theming, stateManagement } = options;
4
+ const { fillBars } = theming;
5
+
6
+ let description = '';
7
+ if (template === 'single-view' || template === 'blank') {
8
+ description = `
9
+ <p>Here is your blank ZMP app. Let's see what we have here.</p>
10
+ `;
11
+ }
12
+
13
+ return indent(
14
+ 0,
15
+ `
16
+ import React from 'react';
17
+ ${
18
+ template === 'blank'
19
+ ? `
20
+ `.trim()
21
+ : `
22
+ import {
23
+ List,
24
+ } from 'zaui';${stateManagement === 'recoil' ? `
25
+ import { useRecoilValue } from 'recoil';
26
+ import { userState } from '../state';` : ''}
27
+ import UserCard from '../components/user-card';
28
+ `.trim()
29
+ }
30
+
31
+ const HomePage = () => {
32
+ ${template !== 'blank' ? (stateManagement === 'recoil' ? "const user = useRecoilValue(userState);" : "const user = useStore('user');") : ''}
33
+ return (
34
+ <div name="home" className="page">
35
+ Hello Zalo Mini APp
36
+ </div>
37
+ );
38
+ }
39
+
40
+ export default HomePage;
41
+ `
42
+ ).trim();
43
+ };
@@ -0,0 +1,45 @@
1
+ const templateIf = require('../../utils/template-if');
2
+ const indent = require('../../utils/indent');
3
+ const appParameters = require('../app-parameters');
4
+
5
+ module.exports = (options) => {
6
+ const { template, theming, stateManagement } = options;
7
+
8
+ // Views
9
+
10
+
11
+ return indent(
12
+ 0,
13
+ `
14
+ import React from 'react';
15
+ import {BrowserRouter, Routes, Route} from 'react-router-dom'
16
+ import HomePage from '../pages';
17
+ import config from '../config';
18
+
19
+ ${['blank', 'single-view'].indexOf(template) >= 0
20
+ && `
21
+ import { App } from 'zaui';
22
+ `.trim()
23
+ }
24
+ ${templateIf(stateManagement === 'recoil', () => `import { RecoilRoot } from 'recoil';`,'')}
25
+
26
+ const MyApp = () => {
27
+
28
+ ${`
29
+ return (${stateManagement === 'recoil' ? `
30
+ <RecoilRoot>` : ''}
31
+ ${indent(stateManagement === 'recoil' ? 2 : 0, `<App ${theming.darkTheme ? 'themeDark' : ''}>`)}
32
+ <BrowserRouter basename={config.basePath}>
33
+ <Routes>
34
+ <Route path="/" element={<HomePage></HomePage>}></Route>
35
+ </Routes>
36
+ </BrowserRouter>
37
+ ${indent(stateManagement === 'recoil' ? 2 : 0, `</App>`)}${stateManagement === 'recoil' ? `
38
+ </RecoilRoot>` : ''}
39
+ );
40
+ `.trim()}
41
+ }
42
+ export default MyApp;
43
+ `
44
+ ).trim();
45
+ };
@@ -0,0 +1,10 @@
1
+ module.exports = (options) => {
2
+ const { template } = options;
3
+
4
+ if (template === 'blank') {
5
+ return [];
6
+ }
7
+ const routes = ['/user', '/form', '/dynamic-route', '/about', '/404'];
8
+
9
+ return routes;
10
+ };
@@ -0,0 +1,39 @@
1
+ const indent = require('../../utils/indent');
2
+ const stylesExtension = require('../../utils/styles-extension');
3
+
4
+ module.exports = (options) => {
5
+ const { cssPreProcessor, theming, customBuild, includeTailwind } = options;
6
+
7
+ let scripts = '';
8
+
9
+ scripts += indent(
10
+ 0,
11
+ `
12
+ // Import React and ReactDOM
13
+ import React from 'react';
14
+ import { createRoot } from 'react-dom/client';
15
+ ${includeTailwind ? `
16
+
17
+ // Import tailwind styles
18
+ import './css/tailwind.css';` : ''}
19
+
20
+ import 'zaui/zaui.css';
21
+
22
+ import './css/app.${stylesExtension(cssPreProcessor)}';
23
+
24
+ // Import App Component
25
+ import App from './components/app.jsx';
26
+ import appConfig from '../app-config.json';
27
+
28
+ if (!window.APP_CONFIG) {
29
+ window.APP_CONFIG = appConfig;
30
+ }
31
+
32
+ // Mount React App
33
+ const root = createRoot(document.getElementById('app'));
34
+ root.render(React.createElement(App));
35
+ `
36
+ );
37
+
38
+ return scripts.trim();
39
+ };
@@ -0,0 +1,47 @@
1
+ const indent = require('../../../utils/indent');
2
+
3
+ // eslint-disable-next-line no-unused-vars
4
+ module.exports = (options) => {
5
+ return indent(
6
+ 0,
7
+ `
8
+ import React from 'react'
9
+ import { Sheet, Button } from 'zaui';
10
+
11
+ const AboutPage = (props) => {
12
+ const [actionSheetOpened, setActionSheetOpened] = React.useState(false);
13
+
14
+ return (
15
+ <div name="page">
16
+ <div>
17
+ <Button
18
+ type="secondary"
19
+ size="full-width"
20
+ onClick={() => setActionSheetOpened(true)}
21
+ >
22
+ Back
23
+ </Button>
24
+ </div>
25
+ <Sheet
26
+ visible={actionSheetOpened}
27
+ onClose={() => setActionSheetOpened(false)}
28
+ id="actions-two-groups"
29
+ title="Bottom Sheet Modal"
30
+ >
31
+ <div style={{padding: '16px'}}>
32
+ Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's s
33
+ </div>
34
+ <div style={{padding: '16px'}}>
35
+ <Button onClick={ () => setActionSheetOpened(false)}>
36
+ Cancel
37
+ </Buton>
38
+ </div>
39
+ </Sheet>
40
+ </div>
41
+ )
42
+ }
43
+
44
+ export default AboutPage;
45
+ `
46
+ ).trim();
47
+ };
@@ -0,0 +1,62 @@
1
+ const indent = require('../../../utils/indent');
2
+
3
+ module.exports = (options) => {
4
+ const { template } = options
5
+ return indent(
6
+ 0,
7
+ `
8
+ import React from 'react';
9
+ import { Button,Input } from 'zaui';
10
+ import { useRecoilState } from 'recoil';
11
+ import { userState } from '../state';
12
+
13
+ const FormPage = () => {
14
+ const [user, setUser] = useRecoilState(userState)
15
+
16
+ const [form, setForm] = React.useState({ ...user });
17
+
18
+ const handleChangeInput = (field, value) => {
19
+ setForm({ ...form, [field]: value })
20
+ }
21
+
22
+ const handleSubmit = () => {
23
+ alert('saved');
24
+ setUser(form);
25
+ toast.current.open()
26
+ }
27
+
28
+ return (
29
+ <div className="page">
30
+ <div>
31
+ <div >
32
+ <Input
33
+ id="name"
34
+ label="Name"
35
+ type="text"
36
+ placeholder="Zalo"
37
+ value={form?.name}
38
+ onChange={(e) => handleChangeInput('name', e.target.value)}
39
+ />
40
+ <Input
41
+ label="Avatar"
42
+ type="text"
43
+ placeholder="zalo@zalo.me"
44
+ value={form?.avatar}
45
+ onChange={(e) => handleChangeInput('avatar', e.target.value)}
46
+ />
47
+ <div >
48
+ <Button size="full-width" type="primary" onClick={handleSubmit}>
49
+ Submit
50
+ </Button>
51
+ </Box>
52
+ </div>
53
+ </div>
54
+ </div>
55
+ )
56
+ }
57
+
58
+ export default FormPage;
59
+ `
60
+ ).trim();
61
+
62
+ };
@@ -0,0 +1,9 @@
1
+ const copyUser = require('./user');
2
+ const copyForm = require('./form');
3
+ const copyAbout = require('./about');
4
+
5
+ module.exports = {
6
+ copyUser,
7
+ copyForm,
8
+ copyAbout,
9
+ };
@@ -0,0 +1,46 @@
1
+ const indent = require('../../../utils/indent');
2
+
3
+ module.exports = (options) => {
4
+ const { template, stateManagement } = options;
5
+ const hideNavBar =
6
+ template === 'tabs'
7
+ ? `
8
+ onPageBeforeIn={()=>{
9
+ //hide navbar
10
+ zmp.toolbar.hide('#app-tab-bar')
11
+ }}`
12
+ : '';
13
+ const modules = ['Avatar', 'List'];
14
+
15
+ return indent(
16
+ 0,
17
+ `
18
+ import React from 'react'
19
+ import { ${modules.join(', ')} } from 'zaui'
20
+ '${stateManagement === 'recoil' ? `
21
+ import { useRecoilValue } from 'recoil';
22
+ import { userState } from '../state';` : ''}
23
+
24
+ const UserPage = () => {
25
+ ${stateManagement === 'recoil' ? 'const user = useRecoilValue(userState);' : `const user = {}`}
26
+
27
+ return (
28
+ <div
29
+ className="page"
30
+ >
31
+ <div style={{ textAlign: 'center', marginTop: 32 }}>
32
+ <Avatar story online src={user.avatar.startsWith('http') ? user.avatar : null}>{user.avatar}</Avatar>
33
+ <h3 style={{ marginTop: 8 }}>{user.name}</h3>
34
+ </div>
35
+ <List>
36
+ <List.Item title="Display name" subtitle={user.name} />
37
+ <List.Item title="ID" subtitle={user.id} />
38
+ </List>
39
+ </div>
40
+ )
41
+ }
42
+
43
+ export default UserPage;
44
+ `
45
+ ).trim();
46
+ };
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from 'vite'
2
+ import reactRefresh from '@vitejs/plugin-react-refresh'
3
+
4
+ // https://vitejs.dev/config/
5
+ export default () => {
6
+ return defineConfig({
7
+ root: './src',
8
+ base: '',
9
+ plugins: [reactRefresh()],
10
+ })
11
+ }
@@ -2,11 +2,17 @@ const generateNpmScripts = require('./generate-npm-scripts');
2
2
  const { generateTailWindScripts } = require('./generate-npm-scripts');
3
3
 
4
4
  module.exports = function generatePackageJson(options) {
5
- const { name, framework, cssPreProcessor, includeTailwind, stateManagement } =
6
- options;
5
+ const {
6
+ name,
7
+ framework,
8
+ cssPreProcessor,
9
+ includeTailwind,
10
+ stateManagement,
11
+ zaui,
12
+ } = options;
7
13
 
8
14
  // Dependencies
9
- const dependencies = ['zmp-framework', 'zmp-sdk', 'swiper'];
15
+ const dependencies = zaui ? [] : ['zmp-framework', 'zmp-sdk', 'swiper'];
10
16
  const dependenciesVue = ['vue@3', 'zmp-framework'];
11
17
  const dependenciesReact = ['react', 'react-dom', 'prop-types'];
12
18
  const dependenciesReactTs = ['@types/react', '@types/react-dom'];
@@ -43,7 +49,7 @@ module.exports = function generatePackageJson(options) {
43
49
  // DevDependencies
44
50
  const devDependenciesCore = ['zmp-loader'];
45
51
  const devDependenciesReact = ['@vitejs/plugin-react-refresh'];
46
- const devDependenciesVue = ['@vitejs/plugin-vue', '@vue/compiler-sfc'];
52
+ const devDependenciesVue = ['@vitejs/plugin-vue@2.3.3', '@vue/compiler-sfc'];
47
53
 
48
54
  if (framework === 'react' || framework === 'react-typescript')
49
55
  devDependencies.push(
@@ -61,6 +67,13 @@ module.exports = function generatePackageJson(options) {
61
67
  ...(includeTailwind ? tailwindDependencies : [])
62
68
  );
63
69
 
70
+ if (zaui) {
71
+ dependencies.push('react-router-dom', 'recoil');
72
+ devDependencies.push(
73
+ ...[devDependenciesReact],
74
+ ...(includeTailwind ? tailwindDependencies : [])
75
+ );
76
+ }
64
77
  // Scripts
65
78
  const scripts = {};
66
79
  generateNpmScripts().forEach((s) => {
@@ -99,7 +112,13 @@ module.exports = function generatePackageJson(options) {
99
112
  "Samsung >= 5"
100
113
  ],
101
114
  "scripts" : ${JSON.stringify(scripts)},
102
- "dependencies": {},
115
+ "dependencies": {
116
+ ${
117
+ zaui
118
+ ? `"zaui": "https://stc-zmp.zadn.vn/zaui/zaui-1.0.0-alpha.1.tgz"`
119
+ : ''
120
+ }
121
+ },
103
122
  "devDependencies": {}
104
123
  }
105
124
  `.trim();
@@ -2,7 +2,9 @@
2
2
  const inquirer = require('inquirer');
3
3
  var chalk = require('chalk');
4
4
 
5
- const moreOptionsText = chalk.italic(`\t- Including Tailwind CSS\n\t- Including Recoil (React only)`);
5
+ const moreOptionsText = chalk.italic(
6
+ `\t- Including Tailwind CSS\n\t- Including Recoil (React only)`
7
+ );
6
8
  const questions = [
7
9
  {
8
10
  type: 'list',
@@ -219,7 +221,9 @@ const questions = [
219
221
  {
220
222
  type: 'list',
221
223
  name: 'stateManagement',
222
- when: (opts) => opts.moreOptions && (opts.framework === 'react' || opts.framework === 'react-typescript'),
224
+ when: (opts) =>
225
+ opts.moreOptions &&
226
+ (opts.framework === 'react' || opts.framework === 'react-typescript'),
223
227
  message: 'Which state management library would you like to use?',
224
228
  default: 'store',
225
229
  choices: [
@@ -234,9 +238,127 @@ const questions = [
234
238
  ],
235
239
  },
236
240
  ];
241
+ const zauiQuestions = [
242
+ {
243
+ type: 'list',
244
+ name: 'newProject',
245
+ message: 'What action you want to do?',
246
+ choices: [
247
+ { name: 'Create a new ZMP project', value: true },
248
+ { name: 'Using ZMP to deploy only', value: false },
249
+ ],
250
+ },
251
+ {
252
+ type: 'input',
253
+ name: 'name',
254
+ message: 'App (project) name:',
255
+ default: 'My App',
256
+ validate(input) {
257
+ return new Promise((resolve, reject) => {
258
+ if (!input) reject(new Error('App name is required'));
259
+ else resolve(true);
260
+ });
261
+ },
262
+ },
263
+ // Framework
264
+ {
265
+ type: 'list',
266
+ name: 'zaui',
267
+ when: (opts) => opts.newProject,
268
+ message: 'What type of framework do you prefer?',
269
+ choices: [
270
+ {
271
+ name: 'ZMP with React',
272
+ value: 'react',
273
+ },
274
+ {
275
+ name: 'ZMP with React (TypeScript)',
276
+ value: 'react-typescript',
277
+ disabled: true,
278
+ },
279
+ ],
280
+ },
237
281
 
238
- module.exports = function getOptions() {
239
- return inquirer.prompt(questions).then((options) => {
282
+ // Template
283
+ {
284
+ type: 'list',
285
+ name: 'template',
286
+ when: (opts) => opts.newProject,
287
+ message: 'Choose starter template:',
288
+ choices: [
289
+ {
290
+ name: 'Blank',
291
+ value: 'blank',
292
+ },
293
+ {
294
+ name: 'Single View',
295
+ value: 'single-view',
296
+ },
297
+ ],
298
+ },
299
+ {
300
+ type: 'list',
301
+ name: 'cssPreProcessor',
302
+ when: (opts) => opts.newProject,
303
+ message: 'Do you want to setup CSS Pre-Processor',
304
+ default: false,
305
+ choices: [
306
+ {
307
+ name: 'No, i am good with CSS',
308
+ value: false,
309
+ },
310
+ {
311
+ name: 'Less',
312
+ value: 'less',
313
+ },
314
+ {
315
+ name: 'SCSS (SASS)',
316
+ value: 'scss',
317
+ },
318
+ {
319
+ name: 'Stylus',
320
+ value: 'stylus',
321
+ },
322
+ ],
323
+ },
324
+ {
325
+ type: 'list',
326
+ name: 'moreOptions',
327
+ when: (opts) => opts.newProject,
328
+ message: `More Options?\n${moreOptionsText}`,
329
+ default: false,
330
+ choices: [
331
+ {
332
+ name: 'No, I want to complete initializing process now',
333
+ value: false,
334
+ },
335
+ {
336
+ name: 'Yes, I want to get more options',
337
+ value: true,
338
+ },
339
+ ],
340
+ },
341
+ {
342
+ type: 'list',
343
+ name: 'includeTailwind',
344
+ when: (opts) => opts.moreOptions,
345
+ message: 'Do you want to include Tailwind CSS?',
346
+ default: true,
347
+ choices: [
348
+ {
349
+ name: 'Yes, I want to iclude Tailwind CSS',
350
+ value: true,
351
+ },
352
+ {
353
+ name: 'No',
354
+ value: false,
355
+ },
356
+ ],
357
+ },
358
+ ];
359
+ module.exports = function getOptions(zaui = false) {
360
+ const listQuestion = zaui ? zauiQuestions : questions;
361
+ return inquirer.prompt(listQuestion).then((options) => {
240
362
  options.theming = {
241
363
  customColor: options.themingCustomColor,
242
364
  color:
@@ -253,6 +375,9 @@ module.exports = function getOptions() {
253
375
  if (!options.stateManagement) {
254
376
  options.stateManagement = 'store';
255
377
  }
378
+ if (options.zaui) {
379
+ options.stateManagement = 'recoil';
380
+ }
256
381
  delete options.themingCustomColor;
257
382
  delete options.themingColor;
258
383
  delete options.themingIconFonts;
package/index.js CHANGED
@@ -25,6 +25,7 @@ const os = require('os');
25
25
  // const server = require('./ui/server');
26
26
  const pkg = require('./package.json');
27
27
  const config = require('./config');
28
+ const createAppUsingZaui = require('./create/init-zaui');
28
29
 
29
30
  const cwd = process.cwd();
30
31
 
@@ -81,6 +82,29 @@ program
81
82
  process.exit(0);
82
83
  });
83
84
 
85
+ program
86
+ .version(pkg.version)
87
+ .usage('<command> [options]')
88
+ .command('init-zaui')
89
+ .option('--skipUpdate', 'Skip checking for update of zmp-cli')
90
+ .description('Init ZMP project using Zaui UI library')
91
+ .action(async (options) => {
92
+ if (!options.skipUpdate) await checkUpdate();
93
+
94
+ const currentProject = getCurrentProject(cwd);
95
+ if (currentProject) {
96
+ log.text(
97
+ `${logSymbols.error} ZMP project already set up in current directory`
98
+ );
99
+ process.exit(1);
100
+ }
101
+ const optsLogin = await getLoginOptions();
102
+ await loginApp({ cwd, ...optsLogin }, logger);
103
+ const opts = await getOptions(true);
104
+ await createAppUsingZaui({ cwd, ...opts }, logger);
105
+ process.exit(0);
106
+ });
107
+
84
108
  program
85
109
  .usage('<command> [options]')
86
110
  .command('login')
package/login/index.js CHANGED
@@ -114,6 +114,7 @@ module.exports = async (options = {}, logger, { exitOnError = true } = {}) => {
114
114
  clearInterval(intervalCheckLogin);
115
115
  const token = resData.data && resData.data.jwt;
116
116
  envUtils.setEnv(config.env.token, token);
117
+ envUtils.setEnv(config.env.accessToken, token);
117
118
  logger.statusDone('Login Success!');
118
119
  return resolve();
119
120
  }
@@ -148,6 +149,7 @@ module.exports = async (options = {}, logger, { exitOnError = true } = {}) => {
148
149
  const dataDecoded = jwt.decode(token);
149
150
  envUtils.setEnv(config.env.appId, dataDecoded.appId);
150
151
  envUtils.setEnv(config.env.token, token);
152
+ envUtils.setEnv(config.env.accessToken, options.token);
151
153
  } catch (error) {
152
154
  return reject(error);
153
155
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zmp-cli",
3
- "version": "3.10.0-rc.0",
3
+ "version": "3.11.0-rc.2",
4
4
  "description": "ZMP command line utility (CLI)",
5
5
  "main": "index.js",
6
6
  "bin": {