@akinon/projectzero 1.0.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/.prettierrc ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "bracketSameLine": false,
3
+ "tabWidth": 2,
4
+ "singleQuote": true,
5
+ "bracketSpacing": true,
6
+ "semi": true,
7
+ "useTabs": false,
8
+ "arrowParens": "always",
9
+ "endOfLine": "lf",
10
+ "proseWrap": "never",
11
+ "trailingComma": "none"
12
+ }
package/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # Project Zero CLI
2
+
3
+ This is the command line interface for Project Zero Next.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g @akinon/projectzero
9
+ ```
10
+
11
+ ## Available Commands
12
+
13
+ ### `create`
14
+ It creates a new project.
15
+
16
+ ```bash
17
+ npx @akinon/projectzero --create
18
+ ```
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const argv = require("yargs").argv;
6
+
7
+ export default () => {
8
+ /**
9
+ * @param {string} src The path to the thing to copy.
10
+ * @param {string} dest The path to the new copy.
11
+ * @param {string} lng Get Language
12
+ */
13
+ const addLanguage = (src: string, dest: string, lng: string) => {
14
+ const exists = fs.existsSync(src);
15
+ const stats = exists && fs.statSync(src);
16
+ const isDirectory = exists && stats.isDirectory();
17
+ if (isDirectory) {
18
+ fs.mkdirSync(dest);
19
+ fs.readdirSync(src).forEach((childItemName: string) => {
20
+ addLanguage(
21
+ path.join(src, childItemName),
22
+ path.join(dest, childItemName),
23
+ lng
24
+ );
25
+ });
26
+
27
+ console.log(
28
+ "\x1b[32m%s\x1b[0m",
29
+ `\n✓ Added language option ${lng.toUpperCase()}.\nDon't forget to translate '${lng.toUpperCase()}' translation files inside 'public/locales/${lng.toUpperCase()}'\n`
30
+ );
31
+
32
+ console.log("\x1b[33m%s\x1b[0m", "Project Zero - Akinon\n");
33
+ } else {
34
+ fs.copyFileSync(src, dest);
35
+ }
36
+ };
37
+
38
+ const i18nDocumentUpdate = (lng: string) => {
39
+ /* next-i18next.config.js */
40
+ const workingDir = path.resolve(process.cwd());
41
+
42
+ const i18nPath = path.resolve(workingDir, "next-i18next.config.js");
43
+
44
+ if (!fs.existsSync(i18nPath)) {
45
+ return;
46
+ }
47
+
48
+ const i18nData = fs.readFileSync(i18nPath, {
49
+ encoding: "utf8",
50
+ flag: "r",
51
+ });
52
+
53
+ fs.writeFileSync(
54
+ i18nPath,
55
+ i18nData.replace(/locales: '*.'/, `locales: ['${lng}', '`)
56
+ );
57
+
58
+ /* settings.js */
59
+
60
+ const settingsPath = path.resolve(workingDir, "src/settings.js");
61
+
62
+ if (!fs.existsSync(settingsPath)) {
63
+ return;
64
+ }
65
+
66
+ const settingsData = fs.readFileSync(settingsPath, {
67
+ encoding: "utf8",
68
+ flag: "r",
69
+ });
70
+
71
+ const data = `{ label: '${lng.toUpperCase()}', value: '${lng.toLowerCase()}', apiValue: '${
72
+ argv.addLanguage
73
+ }' }`;
74
+
75
+ const changes = "languages: [";
76
+
77
+ fs.writeFileSync(
78
+ settingsPath,
79
+ settingsData.replace(changes, `languages: [\n\t\t${data},`)
80
+ );
81
+ };
82
+
83
+ const init = () => {
84
+ if (!argv.addLanguage) return;
85
+ let lng = argv.addLanguage;
86
+ lng = lng.split("-")[0];
87
+
88
+ const dest = `public/locales/${lng}`;
89
+ const exists = fs.existsSync(dest);
90
+
91
+ try {
92
+ if (exists)
93
+ throw new Error(`${lng.toUpperCase()} has already been added`);
94
+ addLanguage("public/locales/en", `public/locales/${lng}`, lng);
95
+ i18nDocumentUpdate(lng);
96
+ } catch (err) {
97
+ const typedError = err as Error;
98
+
99
+ console.log(
100
+ "\n\x1b[31m%s\x1b[0m",
101
+ `${
102
+ typedError.message
103
+ ? typedError.message + "\n"
104
+ : "Something went wrong.\n"
105
+ }`
106
+ );
107
+ }
108
+ };
109
+
110
+ init();
111
+ };
@@ -0,0 +1,33 @@
1
+ import * as fs from 'fs';
2
+ import path from 'path';
3
+
4
+ const yargs = require('yargs/yargs');
5
+ const { hideBin } = require('yargs/helpers');
6
+ const args = yargs(hideBin(process.argv)).argv;
7
+
8
+ export default () => {
9
+ const workingDir = path.resolve(process.cwd());
10
+ const settingsPath = path.resolve(workingDir, './src/settings.js');
11
+
12
+ if (!fs.existsSync(settingsPath)) {
13
+ return;
14
+ }
15
+
16
+ const settingsData = fs.readFileSync(settingsPath, {
17
+ encoding: 'utf8',
18
+ flag: 'r'
19
+ });
20
+
21
+ fs.writeFileSync(
22
+ settingsPath,
23
+ settingsData.replace(
24
+ /commerceUrl: '.*'/,
25
+ `commerceUrl: '${args.commerceUrl}'`
26
+ )
27
+ );
28
+
29
+ console.log(
30
+ '\x1b[32m%s\x1b[0m',
31
+ `\n ✓ Commerce URL is set to ${args.commerceUrl}.\n\n`
32
+ );
33
+ };
@@ -0,0 +1,228 @@
1
+ import path from 'path';
2
+ import * as fs from 'fs';
3
+ import * as readline from 'readline';
4
+ import { slugify } from '../utils';
5
+ import { execSync } from 'child_process';
6
+
7
+ const exec = require('child_process').exec;
8
+ const loadingSpinner = require('loading-spinner');
9
+
10
+ interface Question {
11
+ text: string;
12
+ optional: boolean;
13
+ answerKey: string;
14
+ hint?: string;
15
+ }
16
+
17
+ interface Answers {
18
+ [key: string]: string;
19
+ }
20
+
21
+ const repositorySlug = 'projectzeropwa';
22
+ const tempDirName = '.pz-temp';
23
+ const workingDir = path.resolve(process.cwd());
24
+ const rl = readline.createInterface({
25
+ input: process.stdin,
26
+ output: process.stdout
27
+ });
28
+
29
+ const ANSWERS: Answers = {};
30
+
31
+ const QUESTIONS: Array<Question> = [
32
+ {
33
+ text: 'Brand name',
34
+ optional: false,
35
+ answerKey: 'brandName',
36
+ hint: 'eg. Project Zero'
37
+ },
38
+ {
39
+ text: 'Project description',
40
+ optional: true,
41
+ answerKey: 'projectDescription'
42
+ },
43
+ {
44
+ text: 'Commerce URL (SERVICE_BACKEND_URL)',
45
+ optional: true,
46
+ answerKey: 'commerceUrl',
47
+ hint: 'Can be changed later'
48
+ }
49
+ ];
50
+
51
+ const getAnswers = () => {
52
+ return new Promise<Answers>((resolve) => {
53
+ const question = QUESTIONS[Object.keys(ANSWERS).length];
54
+
55
+ if (!question) {
56
+ rl.close();
57
+ resolve(ANSWERS);
58
+ return;
59
+ }
60
+
61
+ let questionText = question.text;
62
+
63
+ if (question.hint) {
64
+ questionText += ` (${question.hint})`;
65
+ }
66
+
67
+ if (question.optional) {
68
+ questionText += ` (optional)`;
69
+ }
70
+
71
+ rl.question(`${questionText}: `, (answer: string) => {
72
+ if (!question.optional && !answer.length) {
73
+ console.log('\x1b[31m%s\x1b[0m', `* This field is required\n`);
74
+ resolve(getAnswers());
75
+ } else {
76
+ ANSWERS[question.answerKey] = answer;
77
+ resolve(getAnswers());
78
+ }
79
+ });
80
+ });
81
+ };
82
+
83
+ const cloneRepository = () =>
84
+ new Promise<{ error?: Error }>((resolve) => {
85
+ execSync(`mkdir ${tempDirName}`);
86
+
87
+ exec(
88
+ `cd ${tempDirName} && git clone git@bitbucket.org:akinonteam/${repositorySlug}.git`,
89
+ function (err: any, stdout: any, stderr: any) {
90
+ if (err != null) {
91
+ resolve({ error: new Error(err) });
92
+ } else if (typeof stderr != 'string') {
93
+ resolve({ error: new Error(stderr) });
94
+ } else {
95
+ resolve({});
96
+ }
97
+ }
98
+ );
99
+ });
100
+
101
+ const updatePackageJson = (brandName: string) => {
102
+ const packageJsonPath = path.resolve(
103
+ workingDir,
104
+ `./${tempDirName}/${repositorySlug}/package.json`
105
+ );
106
+
107
+ const packageJsonData = fs.readFileSync(packageJsonPath, {
108
+ encoding: 'utf8',
109
+ flag: 'r'
110
+ });
111
+
112
+ fs.writeFileSync(
113
+ packageJsonPath,
114
+ packageJsonData.replace(/"name": ".*"/, `"name": "${slugify(brandName)}"`)
115
+ );
116
+ };
117
+
118
+ const updateAkinonJson = (brandName: string, projectDescription: string) => {
119
+ const akinonJsonPath = path.resolve(
120
+ workingDir,
121
+ `./${tempDirName}/${repositorySlug}/akinon.json`
122
+ );
123
+
124
+ const akinonJsonData = fs.readFileSync(akinonJsonPath, {
125
+ encoding: 'utf8',
126
+ flag: 'r'
127
+ });
128
+
129
+ let updatedData = akinonJsonData.replace(
130
+ /"name": ".*"/,
131
+ `"name": "${brandName}"`
132
+ );
133
+
134
+ if (projectDescription) {
135
+ updatedData = updatedData.replace(
136
+ /"description": ".*"/,
137
+ `"description": "${projectDescription}"`
138
+ );
139
+ }
140
+
141
+ fs.writeFileSync(akinonJsonPath, updatedData);
142
+ };
143
+
144
+ const copyEnv = (commerceUrl: string) => {
145
+ const repositoryPath = path.resolve(
146
+ workingDir,
147
+ `./${tempDirName}/${repositorySlug}`
148
+ );
149
+
150
+ const envExamplePath = path.resolve(repositoryPath, `./.env.example`);
151
+ const envPath = path.resolve(repositoryPath, `./.env`);
152
+
153
+ const envData = fs.readFileSync(envExamplePath, {
154
+ encoding: 'utf8',
155
+ flag: 'r'
156
+ });
157
+
158
+ if (commerceUrl) {
159
+ fs.writeFileSync(
160
+ envExamplePath,
161
+ envData.replace(
162
+ /SERVICE_BACKEND_URL=.*/,
163
+ `SERVICE_BACKEND_URL=${commerceUrl}`
164
+ )
165
+ );
166
+ }
167
+
168
+ fs.copyFileSync(envExamplePath, envPath);
169
+ };
170
+
171
+ const updatePlugins = () => {
172
+ const pluginsPath = path.resolve(
173
+ workingDir,
174
+ `./${tempDirName}/${repositorySlug}/src/plugins.js`
175
+ );
176
+
177
+ fs.writeFileSync(pluginsPath, 'module.exports = [];');
178
+ };
179
+
180
+ export default async () => {
181
+ const answers = await getAnswers();
182
+
183
+ loadingSpinner.start();
184
+
185
+ await cloneRepository();
186
+ updatePackageJson(answers.brandName);
187
+ updateAkinonJson(answers.brandName, answers.projectDescription);
188
+ copyEnv(answers.commerceUrl);
189
+ updatePlugins();
190
+
191
+ const slugifiedBrandName = slugify(answers.brandName);
192
+
193
+ fs.rmSync(
194
+ path.resolve(workingDir, `./${tempDirName}/${repositorySlug}/.git`),
195
+ {
196
+ recursive: true,
197
+ force: true
198
+ }
199
+ );
200
+
201
+ fs.renameSync(
202
+ path.resolve(workingDir, `./${tempDirName}/${repositorySlug}`),
203
+ path.resolve(workingDir, `./${tempDirName}/${slugifiedBrandName}`)
204
+ );
205
+
206
+ fs.cpSync(
207
+ path.resolve(workingDir, `./${tempDirName}/${slugifiedBrandName}`),
208
+ path.resolve(workingDir, `./${slugifiedBrandName}`),
209
+ {
210
+ recursive: true,
211
+ force: true
212
+ }
213
+ );
214
+
215
+ fs.rmSync(path.resolve(workingDir, `./${tempDirName}/`), {
216
+ recursive: true,
217
+ force: true
218
+ });
219
+
220
+ loadingSpinner.stop();
221
+
222
+ console.log(
223
+ '\x1b[32m%s\x1b[0m',
224
+ `\n ✓ ${answers.brandName} project is ready.\n`
225
+ );
226
+
227
+ console.log('\x1b[33m%s\x1b[0m', 'Project Zero - Akinon\n');
228
+ };
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+
6
+ const argv = require("yargs").argv;
7
+
8
+ export default () => {
9
+ const i18nDocumentUpdate = (lng: string) => {
10
+ /* next-i18next.config.js */
11
+ const workingDir = path.resolve(process.cwd());
12
+ const i18nPath = path.resolve(workingDir, "next-i18next.config.js");
13
+
14
+ if (!fs.existsSync(i18nPath)) {
15
+ return;
16
+ }
17
+
18
+ const i18nData = fs.readFileSync(i18nPath, {
19
+ encoding: "utf8",
20
+ flag: "r",
21
+ });
22
+
23
+ const regExpLiteral = "locales:(.+)+";
24
+
25
+ const result = i18nData.match(regExpLiteral);
26
+
27
+ if (result[1].search(lng) < 0) {
28
+ throw new Error(`${lng.toUpperCase()} not available`);
29
+ }
30
+
31
+ let updatedData = i18nData.replace(
32
+ /defaultLocale: '.*'/,
33
+ `defaultLocale: '${lng}'`
34
+ );
35
+
36
+ updatedData = updatedData.replace(
37
+ /fallbackLng: '.*'/,
38
+ `fallbackLng: '${lng}'`
39
+ );
40
+
41
+ fs.writeFileSync(i18nPath, updatedData);
42
+ };
43
+
44
+ const init = () => {
45
+ if (!argv.defaultLanguage) return;
46
+ let lng = argv.defaultLanguage;
47
+
48
+ try {
49
+ i18nDocumentUpdate(lng);
50
+ console.log(
51
+ "\x1b[32m%s\x1b[0m",
52
+ `\n✓ Set as the default language option ${lng.toUpperCase()}.\n`
53
+ );
54
+ console.log("\x1b[33m%s\x1b[0m", "Project Zero - Akinon\n");
55
+ } catch (err) {
56
+ const typedError = err as Error;
57
+
58
+ console.log(
59
+ "\n\x1b[31m%s\x1b[0m",
60
+ `${
61
+ typedError.message
62
+ ? typedError.message + "\n"
63
+ : "Something went wrong.\n"
64
+ }`
65
+ );
66
+ }
67
+ };
68
+
69
+ init();
70
+ };
@@ -0,0 +1,15 @@
1
+ import commerceUrl from './commerce-url';
2
+ import create from './create';
3
+ import addLanguage from './add-language';
4
+ import removeLanguage from './remove-language';
5
+ import defaultLanguage from './default-language';
6
+ import plugins from './plugins';
7
+
8
+ export default {
9
+ commerceUrl,
10
+ create,
11
+ addLanguage,
12
+ removeLanguage,
13
+ defaultLanguage,
14
+ plugins
15
+ };
@@ -0,0 +1,70 @@
1
+ import * as fs from 'fs';
2
+ import path from 'path';
3
+ import { execSync } from 'child_process';
4
+
5
+ const Prompt = require('prompt-checkbox');
6
+
7
+ const rootDir = path.resolve(process.cwd());
8
+ const pluginsFilePath = path.resolve(rootDir, './src/plugins.js');
9
+
10
+ let installedPlugins: Array<string> = [];
11
+
12
+ try {
13
+ installedPlugins = require(path.resolve(rootDir, './src/plugins.js'));
14
+ } catch (error) {}
15
+
16
+ const definedPlugins = [
17
+ {
18
+ name: 'Basket Gift Pack',
19
+ value: 'pz-basket-gift-package'
20
+ }
21
+ ];
22
+
23
+ export default async () => {
24
+ const prompt = new Prompt({
25
+ name: 'plugins',
26
+ message: 'Please check/uncheck plugins to install/uninstall.',
27
+ type: 'checkbox',
28
+ default: installedPlugins.map((p) =>
29
+ definedPlugins.findIndex((dp) => dp.value === p)
30
+ ),
31
+ choices: definedPlugins.map((p, index) => `${index + 1}) ${p.name}`)
32
+ });
33
+
34
+ prompt.ask(async (answers: Array<string>) => {
35
+ const formattedAnswers = answers.map((answer) =>
36
+ answer.replace(/\d\)\s/, '')
37
+ );
38
+
39
+ const values = formattedAnswers.map(
40
+ (answer) => definedPlugins.find((p) => p.name === answer)?.value
41
+ );
42
+
43
+ if (formattedAnswers.length) {
44
+ console.log(`\nInstalling ${formattedAnswers.join(', ')}.`);
45
+ } else {
46
+ console.log(`\nUninstalling all plugins.`);
47
+ }
48
+
49
+ console.log(`\nPlease wait...`);
50
+
51
+ fs.writeFileSync(
52
+ pluginsFilePath,
53
+ `module.exports = ${JSON.stringify(values)};\n`,
54
+ {
55
+ encoding: 'utf-8'
56
+ }
57
+ );
58
+
59
+ execSync('yarn install', { stdio: 'pipe' });
60
+
61
+ console.log(
62
+ '\x1b[32m%s\x1b[0m',
63
+ `\n ✓ ${
64
+ formattedAnswers.length
65
+ ? 'Installed selected plugins'
66
+ : 'Uninstalled all plugins'
67
+ }.\n`
68
+ );
69
+ });
70
+ };
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const argv = require("yargs").argv;
6
+
7
+ export default () => {
8
+ /**
9
+ * @param {string} lng Get Language
10
+ */
11
+
12
+ const removeLanguage = (lng: string) => {
13
+ const workingDir = path.resolve(process.cwd());
14
+
15
+ try {
16
+ fs.rm(`public/locales/${lng}`, { recursive: true }, (err: any) => {
17
+ if (err) {
18
+ console.error(err);
19
+ return;
20
+ }
21
+ });
22
+ } catch (error) {
23
+ console.log("error", error);
24
+ }
25
+
26
+ /* settings.js */
27
+
28
+ const settingsPath = path.resolve(workingDir, "src/settings.js");
29
+
30
+ if (!fs.existsSync(settingsPath)) {
31
+ return;
32
+ }
33
+
34
+ const settingsData = fs.readFileSync(settingsPath, {
35
+ encoding: "utf8",
36
+ flag: "r",
37
+ });
38
+
39
+ const data = `{ label: '${lng.toUpperCase()}', value: '${lng.toLowerCase()}', apiValue: '${lng.toLowerCase()}-${lng.toLowerCase()}' },`;
40
+
41
+ let updatedData = settingsData.replace(data.toString(), "");
42
+
43
+ updatedData = updatedData.replace(/^\s*$(?:\r\n?|\n)/gm, "");
44
+
45
+ fs.writeFileSync(settingsPath, updatedData);
46
+
47
+ /* next-i18next.config.js */
48
+
49
+ const i18nPath = path.resolve(workingDir, "next-i18next.config.js");
50
+
51
+ if (!fs.existsSync(i18nPath)) {
52
+ return;
53
+ }
54
+
55
+ const i18nData = fs.readFileSync(i18nPath, {
56
+ encoding: "utf8",
57
+ flag: "r",
58
+ });
59
+
60
+ fs.writeFileSync(i18nPath, i18nData.replace(`'${lng}',\u0020`, ""));
61
+
62
+ console.log(
63
+ "\x1b[32m%s\x1b[0m",
64
+ `\n✓ Remove language option ${lng.toUpperCase()}.\n`
65
+ );
66
+
67
+ console.log("\x1b[33m%s\x1b[0m", "Project Zero - Akinon\n");
68
+ };
69
+
70
+ const init = () => {
71
+ if (!argv.removeLanguage) return;
72
+ let lng = argv.removeLanguage;
73
+
74
+ try {
75
+ removeLanguage(lng);
76
+ } catch (err) {
77
+ const typedError = err as Error;
78
+
79
+ console.log(
80
+ "\n\x1b[31m%s\x1b[0m",
81
+ `${
82
+ typedError.message
83
+ ? typedError.message + "\n"
84
+ : "Something went wrong.\n"
85
+ }`
86
+ );
87
+ }
88
+ };
89
+
90
+ init();
91
+ };