tsoft-cli 1.1.1 → 1.2.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/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ ## [1.2.0] - 2024-06-12
4
+ ### Added
5
+ - Yeni site ismi girildiğinde geçerli olup olmadığını kontrol eden özellik eklendi.
6
+ - Kullanıcı komut satırı etkileşimleri için `enquirer` kütüphanesi kullanımı güncellendi.
7
+ - Dosya izleme ve tema güncelleme özellikleri geliştirildi.
8
+
9
+ ### Changed
10
+ - `ThemeManager` sınıfı geliştirildi ve yeni yöntemler eklendi.
11
+ - `start.js` dosyasında önemli iyileştirmeler yapıldı.
12
+ - `create.js` dosyasında kullanıcı girişleri doğrulandı ve daha kullanıcı dostu hale getirildi.
13
+
14
+ ### Fixed
15
+ - Birkaç hata düzeltildi ve genel kod kalitesi iyileştirildi.
package/README.md ADDED
@@ -0,0 +1,10 @@
1
+ # T-Soft CLI
2
+
3
+ ## Özet
4
+ T-Soft CLI, T-Soft tarafından geliştirilen bir komut satırı aracıdır. Bu araç ile T-Soft ön yüz geliştirme süreçlerinde kullanılan bazı işlemler otomatize edilmiştir. Bu sayede geliştirme süreçleri hızlandırılmış ve daha verimli hale getirilmiştir.
5
+
6
+ ## Kurulum
7
+ ```bash
8
+ npm install -g tsoft-cli
9
+ ```
10
+
package/bin/index.js CHANGED
@@ -1,29 +1,75 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { program } = require('commander');
3
+ const { Select, Input } = require('enquirer');
4
4
  const { start } = require('../src/commands/start');
5
- const { save } = require('../src/commands/save');
6
5
  const { createTheme } = require('../src/commands/create');
6
+ const { clearScreen, displayAsciiArt } = require('../src/utils');
7
+ const fs = require('fs');
8
+ const path = require('path');
7
9
 
8
- program
9
- .command('start')
10
- .description('Start development environment for a selected site and theme.')
11
- .action(() => {
12
- start();
10
+ async function selectSiteAndTheme() {
11
+ const cwd = process.cwd();
12
+ const sites = fs.readdirSync(cwd).filter(file => {
13
+ return file.charAt(0) !== '.' && !['node_modules', 'build', 'dist'].includes(file);
14
+ }).filter(file => fs.statSync(path.join(cwd, file)).isDirectory());
15
+
16
+ if (sites.length === 0) {
17
+ console.error('Mevcut dizinde site bulunamadı.');
18
+ return null;
19
+ }
20
+
21
+ const sitePrompt = new Select({
22
+ name: 'site',
23
+ message: 'Bir site seçin:',
24
+ choices: sites
25
+ });
26
+
27
+ const selectedSite = await sitePrompt.run();
28
+
29
+ const themes = fs.readdirSync(path.join(cwd, selectedSite)).filter(file => {
30
+ return fs.statSync(path.join(cwd, selectedSite, file)).isDirectory();
13
31
  });
14
32
 
15
- program
16
- .command('save <site> <theme>')
17
- .description('Saving the file changes to the server.')
18
- .action((site, theme) => {
19
- save(site, theme);
33
+ if (themes.length === 0) {
34
+ console.error('Seçilen site dizininde tema bulunamadı.');
35
+ return null;
36
+ }
37
+
38
+ const themePrompt = new Select({
39
+ name: 'theme',
40
+ message: 'Bir tema seçin:',
41
+ choices: themes
20
42
  });
21
43
 
22
- program
23
- .command('create')
24
- .description('Create a new theme')
25
- .action(() => {
26
- createTheme();
44
+ const selectedTheme = await themePrompt.run();
45
+
46
+ return { siteName: selectedSite, themeName: selectedTheme };
47
+ }
48
+
49
+ async function showMainMenu() {
50
+ clearScreen();
51
+
52
+ await displayAsciiArt();
53
+
54
+ const prompt = new Select({
55
+ name: 'command',
56
+ message: 'Bir komut seçin',
57
+ choices: ['Yeni bir tema oluştur', 'Geliştirme ortamını başlat', 'Çıkış']
27
58
  });
28
59
 
29
- program.parse(process.argv);
60
+ const answer = await prompt.run();
61
+
62
+ if (answer === 'Yeni bir tema oluştur') {
63
+ await createTheme();
64
+ } else if (answer === 'Geliştirme ortamını başlat') {
65
+ const selection = await selectSiteAndTheme();
66
+ if (selection) {
67
+ await start(selection.siteName, selection.themeName);
68
+ }
69
+ } else {
70
+ console.log('Programdan çıkılıyor. Hoşça kalın!');
71
+ process.exit(0);
72
+ }
73
+ }
74
+
75
+ showMainMenu();
package/jest.config.js ADDED
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ testEnvironment: 'node',
3
+ };
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "tsoft-cli",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "A theme development cli for developers who want to create themes for T-Soft.",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
7
+ "test": "jest"
8
8
  },
9
9
  "keywords": [],
10
10
  "author": "",
@@ -20,5 +20,9 @@
20
20
  "figlet": "^1.7.0",
21
21
  "fs": "^0.0.1-security"
22
22
  },
23
- "type": "commonjs"
23
+ "type": "commonjs",
24
+ "devDependencies": {
25
+ "jest": "^29.7.0",
26
+ "mock-fs": "^5.2.0"
27
+ }
24
28
  }
@@ -12,10 +12,7 @@ class ClientCredentials {
12
12
 
13
13
  getEnvironment() {
14
14
  return {
15
- site: this.site,
16
- clientId: this.clientId,
17
- clientSecret: this.clientSecret,
18
- bearer: this.bearer
15
+ site: this.site, clientId: this.clientId, clientSecret: this.clientSecret, bearer: this.bearer
19
16
  }
20
17
  }
21
18
 
@@ -32,12 +29,18 @@ class ClientCredentials {
32
29
  return this;
33
30
  }
34
31
 
35
- async register(CLIENT_UUID) {
32
+ async register(CLIENT_UUID, clientConfiguration = {}) {
36
33
 
37
34
  const uri = "api/v3/admin/auth/auth-application";
38
35
  const result = await this.buildPostRequest(uri, {
39
- body: JSON.stringify({
40
- uuid: CLIENT_UUID,
36
+ header: {
37
+ 'Content-Type': 'application/json', 'Accept': 'application/json',
38
+ }, body: JSON.stringify({
39
+ uuid: CLIENT_UUID, configuration: {
40
+ theme: clientConfiguration.theme,
41
+ author: clientConfiguration.author,
42
+ version: clientConfiguration.version
43
+ }
41
44
  })
42
45
  });
43
46
 
@@ -54,8 +57,7 @@ class ClientCredentials {
54
57
  const uri = "api/v3/admin/auth/auth-application/authorize";
55
58
  await this.buildPostRequest(uri, {
56
59
  body: JSON.stringify({
57
- public_key: this.clientId,
58
- secret: this.clientSecret
60
+ public_key: this.clientId, secret: this.clientSecret
59
61
  })
60
62
  }).then((result) => {
61
63
  if (result.status === true) {
@@ -75,12 +77,11 @@ class ClientCredentials {
75
77
  const response = await buildPostRequest(this.site, uri, {
76
78
  body: JSON.stringify({
77
79
  _method: 'GET'
78
- }),
79
- headers: {
80
+ }), headers: {
80
81
  Authorization: `Bearer ${token}`
81
82
  }
82
83
  });
83
- return response.message === 'ok'; // response.ok true ise token geçerli
84
+ return response.message === 'ok';
84
85
  }
85
86
  }
86
87
 
@@ -1,24 +1,55 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
- const { Input } = require('enquirer');
3
+ const { Select, Input } = require('enquirer');
4
+ const {slugify} = require('../utils');
5
+ const { start } = require('./start');
6
+ const ThemeManager = require('../theme-manager');
4
7
 
5
8
  async function createTheme() {
6
- const sitePrompt = new Input({
7
- name: 'siteName',
8
- message: 'Çalışmak istediğiniz sitenin adı:'
9
+ const cwd = process.cwd();
10
+ const sites = fs.readdirSync(cwd).filter(file => {
11
+ return file.charAt(0) !== '.' && !['node_modules', 'build', 'dist'].includes(file);
12
+ }).filter(file => fs.statSync(path.join(cwd, file)).isDirectory());
13
+
14
+ const siteChoices = [...sites, 'Yeni bir site oluştur'];
15
+
16
+ const sitePrompt = new Select({
17
+ name: 'site',
18
+ message: 'Çalışmak istediğiniz sitenin adı:',
19
+ choices: siteChoices
9
20
  });
10
21
 
22
+ const selectedSite = await sitePrompt.run();
23
+
24
+ let siteName;
25
+ if (selectedSite === 'Yeni bir site oluştur') {
26
+ const newSitePrompt = new Input({
27
+ name: 'newSite',
28
+ message: 'Yeni site adı:'
29
+ });
30
+ siteName = await newSitePrompt.run();
31
+
32
+ const themeManager = new ThemeManager(siteName);
33
+ try {
34
+ const isValid = await themeManager.checkSiteValidity(siteName);
35
+ if (!isValid) {
36
+ console.error('Geçersiz site adı. Lütfen geçerli bir site adı girin.');
37
+ return;
38
+ }
39
+ } catch (error) {
40
+ console.error('Site doğrulaması sırasında hata oluştu:', error.message);
41
+ return;
42
+ }
43
+
44
+ } else {
45
+ siteName = selectedSite;
46
+ }
47
+
11
48
  const themeNamePrompt = new Input({
12
49
  name: 'themeName',
13
50
  message: 'Tema adı:'
14
51
  });
15
52
 
16
- const directoryNamePrompt = new Input({
17
- name: 'directoryName',
18
- message: 'Tema dizin ismi (a-z yalnızca İngilizce karakterler):',
19
- validate: input => /^[a-z]+$/.test(input) ? true : 'Yalnızca küçük İngilizce harfler kullanabilirsiniz.'
20
- });
21
-
22
53
  const authorPrompt = new Input({
23
54
  name: 'author',
24
55
  message: 'Author (isteğe bağlı):'
@@ -30,30 +61,31 @@ async function createTheme() {
30
61
  initial: '1.0.0'
31
62
  });
32
63
 
33
- const siteName = await sitePrompt.run();
34
64
  const themeName = await themeNamePrompt.run();
35
- const directoryName = await directoryNamePrompt.run();
36
65
  const author = await authorPrompt.run();
37
66
  const version = await versionPrompt.run();
38
67
 
39
- const cwd = process.cwd();
40
- const themePath = path.join(cwd, siteName, directoryName);
68
+ const themeNameSlug = slugify(themeName);
69
+
70
+ const themePath = path.join(cwd, siteName, themeNameSlug);
41
71
 
42
72
  if (!fs.existsSync(themePath)) {
43
73
  fs.mkdirSync(themePath, { recursive: true });
44
74
  }
45
75
 
46
76
  const themeData = {
47
- siteName,
48
- themeName,
49
- directoryName,
50
- author,
51
- version
77
+ site: siteName,
78
+ theme: themeName,
79
+ author: author,
80
+ version: version
52
81
  };
53
82
 
54
83
  fs.writeFileSync(path.join(themePath, '.theme'), JSON.stringify(themeData, null, 2));
55
84
 
56
85
  console.log(`Tema "${themeName}" başarıyla oluşturuldu.`);
86
+
87
+ // Doğrudan start fonksiyonuna geçiş yap
88
+ await start(siteName, themeNameSlug);
57
89
  }
58
90
 
59
91
  module.exports = {
@@ -1,43 +1,26 @@
1
- const Tsoft = require('../tsoft');
2
- const constants = require('../constants');
3
1
  const fs = require('fs');
4
2
  const path = require('path');
5
- const {Input, Select, Confirm, prompt} = require('enquirer');
6
3
  const chokidar = require('chokidar');
7
- const figlet = require('figlet');
8
4
  const readline = require('readline');
5
+ const { exec } = require('child_process');
6
+ const Tsoft = require('../tsoft');
7
+ const constants = require('../constants');
9
8
  const cliProgress = require('cli-progress');
10
- const {exec} = require('child_process');
11
-
12
- async function displayAsciiArt() {
13
- return new Promise((resolve, reject) => {
14
- figlet('T-SOFT', {
15
- font: 'Standard',
16
- }, (err, data) => {
17
- if (err) {
18
- console.log('Something went wrong...');
19
- console.dir(err);
20
- reject(err);
21
- return;
22
- }
23
- console.log(data);
24
- resolve();
25
- });
26
- });
27
- }
9
+ const { Input } = require('enquirer');
10
+ const { displayAsciiArt, clearScreen, info, slugify } = require('../utils');
28
11
 
29
12
  async function showProgress(tsoft, theme) {
30
13
  const themePath = path.join(process.cwd(), tsoft.site, theme);
31
14
  const fileList = tsoft.fileManager.listFilesRecursive(themePath).filter(file => !file.includes('.env'));
32
15
  const totalFiles = fileList.length;
33
16
 
34
- // Create a new progress bar instance and use shades_classic theme
17
+ // Yeni bir ilerleme çubuğu oluşturun ve shades_classic temasını kullanın
35
18
  const progressBar = new cliProgress.SingleBar({
36
- format: 'Progress |{bar}| {percentage}% || {value}/{total} Files Uploaded',
19
+ format: 'İlerleme |{bar}| {percentage}% || {value}/{total} Dosya Yüklendi',
37
20
  barCompleteChar: '\u2588',
38
21
  barIncompleteChar: '\u2591',
39
22
  hideCursor: true,
40
- clearOnComplete: false
23
+ clearOnComplete: true
41
24
  });
42
25
  progressBar.start(totalFiles, 0);
43
26
 
@@ -47,149 +30,71 @@ async function showProgress(tsoft, theme) {
47
30
  const content = tsoft.fileManager.readFile(file);
48
31
  const env = tsoft.themeManager.getEnvironment(theme);
49
32
 
50
-
51
33
  try {
52
34
  await tsoft.saveFile(theme, fileName, content, env.bearer);
53
35
  } catch (error) {
54
- console.error(`Error uploading file ${fileName}:`, error);
36
+ console.error(`Dosya yükleme hatası ${fileName}:`, error);
55
37
  }
56
38
  progressBar.update(i + 1);
57
39
  }
58
40
 
59
41
  progressBar.stop();
60
42
 
61
- return 'All files uploaded with progress shown successfully';
62
- }
63
-
64
- async function updateTheme(tsoft, theme) {
65
- const directoryName = path.basename(path.join(process.cwd(), tsoft.site, theme));
66
- const response = await tsoft.updateTheme(directoryName);
67
- if (response.status === true) {
68
- return 'Successfully updated';
69
- } else {
70
- throw new Error('Update failed');
71
- }
72
- }
73
-
74
- function clearScreen() {
75
- process.stdout.write('\x1B[2J\x1B[0f');
76
- }
77
-
78
- async function editThemeFile(themeManager, directoryName) {
79
- const themePath = path.join(process.cwd(), themeManager.site, directoryName, '.theme');
80
-
81
- if (!fs.existsSync(themePath)) {
82
- console.error('.theme file not found.');
83
- return;
84
- }
85
-
86
- console.log(`Opening .theme file: ${themePath}`);
87
- const openCommand = process.platform === 'win32' ? 'start' :
88
- process.platform === 'darwin' ? 'open' :
89
- 'xdg-open';
90
-
91
- exec(`${openCommand} ${themePath}`, (err) => {
92
- if (err) {
93
- console.error('Failed to open .theme file:', err);
94
- } else {
95
- console.log('.theme file opened successfully.');
96
- }
97
- });
43
+ return 'Tüm dosyalar ilerleme ile başarıyla yüklendi';
98
44
  }
99
45
 
100
- function info() {
101
- console.log('\n' +
102
- '********************************************************\n' +
103
- '* *\n' +
104
- '* Press "q" to quit watching, "u" to update theme, *\n' +
105
- '* "s" to save the theme, "e" to edit .theme file. *\n' +
106
- '* *\n' +
107
- '********************************************************\n'
108
- );
109
- }
110
-
111
- async function start() {
112
- process.stdout.write('\x1B[2J\x1B[0f');
46
+ async function start(siteName, themeName) {
113
47
  await displayAsciiArt();
114
- const cwd = process.cwd();
115
- const sites = fs.readdirSync(cwd).filter(file => {
116
- return file.charAt(0) !== '.' && !['node_modules', 'build', 'dist'].includes(file);
117
- }).filter(file => fs.statSync(path.join(cwd, file)).isDirectory());
48
+ console.log('Geliştirme ortamı aşağıdaki ayrıntılarla başlatılıyor:');
49
+ console.log(`Site Adı: ${siteName}`);
50
+ console.log(`Tema Adı: ${themeName}`);
118
51
 
119
- if (sites.length === 0) {
120
- console.error('No sites found in the current directory.');
121
- return;
122
- }
123
-
124
- const sitePrompt = new Select({
125
- name: 'selectedSite',
126
- message: 'Please select a site:',
127
- choices: sites
128
- });
129
-
130
- const selectedSite = await sitePrompt.run();
131
-
132
- const sitePath = path.join(cwd, selectedSite);
133
- const themes = fs.readdirSync(sitePath).filter(file => fs.statSync(path.join(sitePath, file)).isDirectory());
134
-
135
- if (themes.length === 0) {
136
- console.error('No themes found in the selected site directory.');
137
- return;
138
- }
139
-
140
- const themePrompt = new Select({
141
- name: 'selectedTheme',
142
- message: 'Please select a theme:',
143
- choices: themes
144
- });
145
-
146
- const selectedTheme = await themePrompt.run();
147
- const themeEnvPath = path.join(sitePath, selectedTheme, `${selectedTheme}.env`);
52
+ const tsoft = new Tsoft(siteName);
53
+ const themeEnvPath = path.join(process.cwd(), siteName, themeName, `${themeName}.env`);
148
54
 
149
- // Check if .env file exists and contains a valid JSON with a bearer attribute
55
+ // .env dosyasının var olup olmadığını ve geçerli bir JSON içerip içermediğini kontrol edin
150
56
  let envData = {};
151
57
  if (fs.existsSync(themeEnvPath)) {
152
58
  try {
153
59
  envData = JSON.parse(fs.readFileSync(themeEnvPath, 'utf-8'));
154
60
  } catch (error) {
155
- console.warn(`Invalid JSON format in ${themeEnvPath}. This file will be deleted and you will need to reauthorize.`);
61
+ console.warn(`${themeEnvPath} dosyasında geçersiz JSON formatı. Bu dosya silinecek ve yeniden yetkilendirmeniz gerekecek.`);
156
62
  fs.unlinkSync(themeEnvPath);
157
- console.info('Invalid environment file deleted. Proceeding with initialization...');
63
+ console.info('Geçersiz ortam dosyası silindi. Başlatma işlemine devam ediliyor...');
158
64
  }
159
65
 
160
66
  if (envData.bearer) {
161
- const tsoft = new Tsoft(selectedSite);
162
- const bearerTokenValid = await tsoft.isTokenValid(selectedTheme);
67
+ const bearerTokenValid = await tsoft.isTokenValid(themeName);
163
68
 
164
69
  if (bearerTokenValid) {
165
- console.info('Existing environment file with a valid bearer token found. Proceeding with existing configuration...');
166
- // Start watching for file changes
167
- await startWatching(selectedSite, selectedTheme, tsoft);
70
+ console.info('Geçerli bir taşıyıcı jetonuna sahip mevcut ortam dosyası bulundu. Mevcut yapılandırma ile devam ediliyor...');
71
+ // Dosya değişikliklerini izlemeye başlayın
72
+ await startWatching(siteName, themeName, tsoft);
168
73
  return;
169
74
  } else {
170
- console.warn('Existing bearer token is invalid. You will need to reauthorize.');
75
+ console.warn('Mevcut taşıyıcı jetonu geçersiz. Yeniden yetkilendirmeniz gerekecek.');
171
76
  fs.unlinkSync(themeEnvPath);
172
77
  }
173
78
  }
174
79
  }
175
80
 
176
- let t = new Tsoft(selectedSite);
177
- const initializeResult = await t.initialize(selectedTheme, {reset: false});
81
+ await tsoft.initialize(themeName);
178
82
 
179
83
  const approvalPrompt = new Input({
180
84
  name: 'approval',
181
- message: 'Please provide the approval code:'
85
+ message: 'Lütfen onay kodunu sağlayın:'
182
86
  });
183
87
 
184
88
  const approval = await approvalPrompt.run();
185
89
 
186
90
  if (approval) {
187
- await constants.APPROVAL.handler(t.clientCredentials.setClientSecret(approval));
188
- t.buildEnvironment();
189
- console.info('You are ready to go');
190
-
191
- // Start watching for file changes
192
- await startWatching(selectedSite, selectedTheme, t);
91
+ await constants.APPROVAL.handler(tsoft.clientCredentials.setClientSecret(approval));
92
+ tsoft.buildEnvironment();
93
+ console.info('Hazırsınız');
94
+ // Dosya değişikliklerini izlemeye başlayın
95
+ await startWatching(siteName, themeName, tsoft);
96
+ } else {
97
+ console.error('Onay kodu gerekli.');
193
98
  }
194
99
  }
195
100
 
@@ -198,31 +103,55 @@ async function startWatching(site, theme, tsoft) {
198
103
  const envPath = path.join(themePath, `${theme}.env`);
199
104
 
200
105
  if (!fs.existsSync(envPath)) {
201
- console.error('Environment file not found. Please initialize the theme first.');
106
+ console.error('Ortam dosyası bulunamadı. Lütfen temayı önce başlatın.');
202
107
  return;
203
108
  }
204
109
 
205
110
  const watcher = chokidar.watch(themePath, {
206
- ignored: /(^|[\/\\])\../, // ignore dotfiles
111
+ ignored: /(^|[\/\\])(\.theme|(.*?)\.env)$/, // .theme ve .env dosyaları yoksayılır
207
112
  persistent: true
208
113
  });
209
114
 
210
115
  watcher.on('change', async (filePath) => {
211
- console.log(`File ${filePath} has been changed`);
116
+ console.log(`Dosya değiştirildi ${filePath}`);
212
117
 
213
118
  const env = JSON.parse(fs.readFileSync(envPath, 'utf-8'));
214
119
  const fileName = filePath.replace(`${themePath}/`, '');
215
120
  const content = fs.readFileSync(filePath).toString();
216
121
 
217
122
  try {
218
- const result = await tsoft.saveFile(theme, fileName, content, env.bearer);
219
- console.log(`File ${fileName} uploaded successfully`);
123
+ await tsoft.saveFile(theme, fileName, content, env.bearer);
124
+ console.log(`Dosya başarıyla yüklendi ${fileName}`);
125
+ } catch (error) {
126
+ console.error(`Dosya yükleme hatası ${fileName}:`, error);
127
+ }
128
+ }).on('unlink', async (filePath) => {
129
+ console.log(`Dosya silindi ${filePath}`);
130
+
131
+ const env = JSON.parse(fs.readFileSync(envPath, 'utf-8'));
132
+ const fileName = filePath.replace(`${themePath}/`, '');
133
+
134
+ try {
135
+ await tsoft.deleteFile(theme, fileName, env.bearer);
136
+ console.log(`Dosya başarıyla silindi ${fileName}`);
137
+ } catch (error) {
138
+ console.error(`Dosya silme hatası ${fileName}:`, error);
139
+ }
140
+ }).on('unlinkDir', async (dirPath) => {
141
+ console.log(`Dizin silindi ${dirPath}`);
142
+
143
+ const env = JSON.parse(fs.readFileSync(envPath, 'utf-8'));
144
+ const dirName = dirPath.replace(`${themePath}/`, '');
145
+
146
+ try {
147
+ await tsoft.deleteDirectory(theme, dirName, env.bearer);
148
+ console.log(`Dizin başarıyla silindi ${dirName}`);
220
149
  } catch (error) {
221
- console.error(`Error uploading file ${fileName}:`, error);
150
+ console.error(`Dizin silme hatası ${dirName}:`, error);
222
151
  }
223
152
  });
224
153
 
225
- console.log(`Watching for file changes in ${themePath}`);
154
+ console.log(`${themePath} dizinindeki dosya değişiklikleri izleniyor`);
226
155
 
227
156
  const rl = readline.createInterface({
228
157
  input: process.stdin,
@@ -233,40 +162,50 @@ async function startWatching(site, theme, tsoft) {
233
162
 
234
163
  rl.on('line', async (input) => {
235
164
  if (input.trim() === 'q') {
236
- console.log('Quitting file watch...');
165
+ console.log('Dosya izleme işlemi sonlandırılıyor...');
237
166
  watcher.close();
238
167
  rl.close();
239
168
  process.exit(0);
240
169
  } else if (input.trim() === 'u') {
241
- console.log('Updating theme...');
170
+ console.log('Tema güncelleniyor...');
242
171
  try {
243
172
  const result = await updateTheme(tsoft, theme);
244
173
  console.log(result);
245
174
  info();
246
175
  } catch (error) {
247
- console.error(`Error updating theme:`, error);
176
+ console.error(`Tema güncelleme hatası:`, error);
248
177
  }
249
178
  } else if (input.trim() === 's') {
250
179
  clearScreen();
251
- console.log('Starting to upload all files with progress...');
180
+ console.log('Tüm dosyalar ilerleme ile yüklenmeye başlanıyor...');
252
181
  try {
253
182
  const result = await showProgress(tsoft, theme);
254
183
  console.log(result);
255
184
  info();
256
185
  } catch (error) {
257
- console.error(`Error showing progress:`, error);
186
+ console.error(`İlerleme gösterim hatası:`, error);
258
187
  }
259
188
  } else if (input.trim() === 'e') {
260
- console.log('Editing .theme file...');
189
+ console.log('.theme dosyası düzenleniyor...');
261
190
  try {
262
191
  await editThemeFile(tsoft.themeManager, theme);
263
192
  } catch (error) {
264
- console.error(`Error editing .theme file:`, error);
193
+ console.error(`.theme dosyası düzenleme hatası:`, error);
265
194
  }
195
+ } else if (input.trim() === 'o') {
196
+ console.log('Tema dizini açılıyor...');
197
+ const openCommand = process.platform === 'win32' ? 'start' :
198
+ process.platform === 'darwin' ? 'open' :
199
+ 'xdg-open';
200
+ exec(`${openCommand} ${themePath}`, (err) => {
201
+ if (err) {
202
+ console.error('Tema dizini açılamadı:', err);
203
+ } else {
204
+ console.log('Tema dizini başarıyla açıldı.');
205
+ }
206
+ });
266
207
  }
267
208
  });
268
209
  }
269
210
 
270
- module.exports = {
271
- start
272
- };
211
+ module.exports = { start };
@@ -1,5 +1,6 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const utils = require('./utils');
3
4
 
4
5
  class FileManager {
5
6
  constructor(site) {
@@ -14,7 +15,7 @@ class FileManager {
14
15
  if (fs.statSync(fullPath).isDirectory()) {
15
16
  this.listFilesRecursive(fullPath, fileList);
16
17
  } else {
17
- fileList.push(fullPath);
18
+ this.isAllowedExtension(fullPath) && fileList.push(fullPath);
18
19
  }
19
20
  });
20
21
  return fileList;
@@ -28,6 +29,11 @@ class FileManager {
28
29
  readFile(file) {
29
30
  return fs.readFileSync(file).toString();
30
31
  }
32
+
33
+ isAllowedExtension(file) {
34
+ const ext = path.extname(file).substring(1);
35
+ return utils.allowedExtensions.includes(ext);
36
+ }
31
37
  }
32
38
 
33
39
  module.exports = FileManager;
@@ -1,5 +1,8 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const {slugify} = require('./utils');
4
+ const http = require('http');
5
+ const https = require('https');
3
6
 
4
7
  class ThemeManager {
5
8
  constructor(site) {
@@ -10,14 +13,12 @@ class ThemeManager {
10
13
  }
11
14
 
12
15
  async initializeTheme(theme, reInit = false) {
13
-
14
16
  this.theme = theme;
15
17
  const themePath = path.join(this.cwd, this.site, theme);
16
18
  const themeEnvPath = path.join(themePath, `${theme}.env`);
17
19
 
18
-
19
20
  if (reInit) {
20
- ///await fs.rm(themePath, {recursive: true, force: true}, (err, file) => {});
21
+ // await fs.rm(themePath, {recursive: true, force: true}, (err, file) => {});
21
22
  }
22
23
 
23
24
  if (!fs.existsSync(path.join(this.cwd, this.site))) {
@@ -38,6 +39,19 @@ class ThemeManager {
38
39
  this.registered = false;
39
40
  }
40
41
  }
42
+
43
+ try {
44
+ const isValid = await this.checkSiteValidity();
45
+ if (!isValid) {
46
+ console.error('Geçersiz site: Siteye ulaşılamıyor.');
47
+ return false;
48
+ }
49
+ } catch (error) {
50
+ console.error('Geçersiz site: Siteye ulaşılamıyor.', error.message);
51
+ return false;
52
+ }
53
+
54
+ return true;
41
55
  }
42
56
 
43
57
  isRegistered() {
@@ -50,25 +64,46 @@ class ThemeManager {
50
64
  }
51
65
 
52
66
  getEnvironment(theme) {
53
- const themeEnvPath = path.join(this.cwd, this.site, theme, `${theme}.env`);
67
+ const themeEnvPath = path.join(this.cwd, this.site, slugify(theme), `${slugify(theme)}.env`);
54
68
  return JSON.parse(fs.readFileSync(themeEnvPath));
55
69
  }
56
70
 
57
71
  getThemeInfo(directoryName) {
58
- const themePath = path.join(process.cwd(), this.site, directoryName, '.theme');
72
+ const themePath = path.join(process.cwd(), this.site, slugify(directoryName), '.theme');
59
73
  if (fs.existsSync(themePath)) {
60
74
  const themeInfo = fs.readFileSync(themePath, 'utf-8');
61
75
  try {
62
76
  return JSON.parse(themeInfo);
63
77
  } catch (error) {
64
- console.error('Error parsing .theme file:', error);
78
+ console.error('.theme dosyasını ayrıştırma hatası:', error);
65
79
  return null;
66
80
  }
67
81
  } else {
68
- console.error('.theme file not found in', themePath);
82
+ console.error('.theme dosyası bulunamadı:', themePath);
69
83
  return null;
70
84
  }
71
85
  }
86
+
87
+ checkSiteValidity() {
88
+ return new Promise((resolve, reject) => {
89
+ const url = new URL(`https://${this.site}/Y/R`);
90
+ const request = url.protocol === 'https:' ? https : http;
91
+
92
+ const req = request.get(url, (res) => {
93
+ if (res.statusCode === 200) {
94
+ resolve(true);
95
+ } else {
96
+ resolve(false);
97
+ }
98
+ });
99
+
100
+ req.on('error', (err) => {
101
+ reject(err);
102
+ });
103
+
104
+ req.end();
105
+ });
106
+ }
72
107
  }
73
108
 
74
109
  module.exports = ThemeManager;
package/src/tsoft.js CHANGED
@@ -8,6 +8,7 @@ const buildPostRequest = require('./request');
8
8
  class Tsoft {
9
9
  static TSOFT_CLI_UUID = '7583a318-8542-4ec8-980c-48ea67031be3';
10
10
  static ONLINESTORE_SAVE_URI = 'api/v3/admin/online-store-2/theme/file-manager?q=save';
11
+ static ONLINESTORE_DELETE_URI = 'api/v3/admin/online-store-2/theme/file-manager?q=delete';
11
12
  static UPDATE_THEME_URI = 'api/v3/admin/online-store-2/theme/update-theme';
12
13
 
13
14
 
@@ -21,7 +22,7 @@ class Tsoft {
21
22
  async initialize(theme, reInit = false) {
22
23
  await this.themeManager.initializeTheme(theme, reInit);
23
24
  if (!this.themeManager.isRegistered()) {
24
- return await this.register(Tsoft.TSOFT_CLI_UUID);
25
+ return await this.register(Tsoft.TSOFT_CLI_UUID, this.themeManager.getThemeInfo(theme));
25
26
  } else {
26
27
  console.warn('You are already registered');
27
28
  }
@@ -35,8 +36,8 @@ class Tsoft {
35
36
  await this.clientCredentials.authorize();
36
37
  }
37
38
 
38
- async register(CLIENT_UUID) {
39
- const response = await this.clientCredentials.register(CLIENT_UUID);
39
+ async register(CLIENT_UUID, clientConfiguration = {}) {
40
+ const response = await this.clientCredentials.register(CLIENT_UUID, clientConfiguration);
40
41
  return response === true ? constants.APPROVAL : null;
41
42
  }
42
43
 
@@ -58,16 +59,14 @@ class Tsoft {
58
59
  content: content
59
60
  })
60
61
  });
61
- console.log({
62
- result: result
63
- })
64
62
  }));
65
63
  }
66
64
 
67
65
  async saveFile(theme, fileName, content, bearerToken) {
68
- const response = await buildPostRequest(this.site, Tsoft.ONLINESTORE_SAVE_URI, {
66
+ return await buildPostRequest(this.site, Tsoft.ONLINESTORE_SAVE_URI, {
69
67
  headers: {
70
68
  Authorization: `Bearer ${bearerToken}`,
69
+ Accept: 'application/json',
71
70
  'Content-Type': 'application/json'
72
71
  },
73
72
  body: JSON.stringify({
@@ -76,8 +75,39 @@ class Tsoft {
76
75
  content: content
77
76
  })
78
77
  });
78
+ }
79
+
80
+ async deleteFile(theme, fileName, bearerToken) {
81
+
82
+ return await buildPostRequest(this.site, Tsoft.ONLINESTORE_DELETE_URI, {
83
+ headers: {
84
+ Authorization: `Bearer ${bearerToken}`,
85
+ 'Content-Type': 'application/json'
86
+ },
87
+ body: JSON.stringify({
88
+ theme: theme,
89
+ items: [
90
+ {type: 'file', path: fileName}
91
+ ]
92
+ })
93
+ });
79
94
 
80
- return response;
95
+ }
96
+
97
+ async deleteDirectory(theme, folderName, bearerToken) {
98
+ return await buildPostRequest(this.site, Tsoft.ONLINESTORE_DELETE_URI, {
99
+ headers: {
100
+ Authorization: `Bearer ${bearerToken}`,
101
+ Accept: 'application/json',
102
+ 'Content-Type': 'application/json'
103
+ },
104
+ body: JSON.stringify({
105
+ theme: theme,
106
+ items: [
107
+ {type: 'dir', path: folderName}
108
+ ]
109
+ })
110
+ });
81
111
  }
82
112
 
83
113
 
@@ -90,19 +120,17 @@ class Tsoft {
90
120
  const env = this.themeManager.getEnvironment(directoryName);
91
121
  const themeInfo = this.themeManager.getThemeInfo(directoryName); // .theme bilgisi alınıyor
92
122
 
93
- const response = await buildPostRequest(this.site, Tsoft.UPDATE_THEME_URI, {
123
+ return await buildPostRequest(this.site, Tsoft.UPDATE_THEME_URI, {
94
124
  headers: {
95
125
  Authorization: `Bearer ${env.bearer}`,
96
- 'Content-Type': 'application/json',
97
- 'Accept': 'application/json'
126
+ 'Accept': 'application/json',
127
+ 'Content-Type': 'application/json'
98
128
  },
99
129
  body: JSON.stringify({
100
130
  theme: directoryName,
101
131
  themeInfo: themeInfo
102
132
  })
103
133
  });
104
-
105
- return response;
106
134
  }
107
135
 
108
136
  }
package/src/utils.js ADDED
@@ -0,0 +1,45 @@
1
+ const figlet = require("figlet");
2
+ module.exports = {
3
+ slugify: function (text) {
4
+ return text.toString().toLowerCase()
5
+ .replace(/\s+/g, '-')
6
+ .replace(/[^\w\-]+/g, '')
7
+ .replace(/\-\-+/g, '-')
8
+ .replace(/^-+/, '')
9
+ .replace(/-+$/, '');
10
+ },
11
+ allowedExtensions: ['theme', 'twig', 'css', 'js', 'md', 'json', 'svg', 'jpeg', 'jpg', 'png', 'gif', 'webp'],
12
+ clearScreen: function () {
13
+ process.stdout.write('\x1B[2J\x1B[0f');
14
+ },
15
+ displayAsciiArt: async function () {
16
+ return new Promise((resolve, reject) => {
17
+ figlet('T-SOFT CLI', {
18
+ font: 'Standard',
19
+ }, (err, data) => {
20
+ if (err) {
21
+ console.log('Something went wrong...');
22
+ console.dir(err);
23
+ reject(err);
24
+ return;
25
+ }
26
+ console.log(data);
27
+ resolve();
28
+ });
29
+ });
30
+ },
31
+ info: function () {
32
+ console.log('\n' +
33
+ '********************************************************\n' +
34
+ '* *\n' +
35
+ '* Dosya izlemeyi sonlandırmak için "q" tuşuna basın, *\n' +
36
+ '* Temayı güncellemek için "u" tuşuna basın, *\n' +
37
+ '* Temayı kaydetmek için "s" tuşuna basın, *\n' +
38
+ '* .theme dosyasını düzenlemek için "e" tuşuna basın, *\n' +
39
+ '* Tema dizinini açmak için "o" tuşuna basın. *\n' +
40
+ '* *\n' +
41
+ '********************************************************\n'
42
+ );
43
+ }
44
+
45
+ }
@@ -0,0 +1,90 @@
1
+ const path = require('path');
2
+ const mockFs = require('mock-fs');
3
+ const FileManager = require('../src/file-manager');
4
+
5
+ describe('FileManager', () => {
6
+ const site = 'testSite';
7
+ const fileManager = new FileManager(site);
8
+
9
+ beforeEach(() => {
10
+ mockFs({
11
+ '/testDir': {
12
+ 'file1.js': 'console.log("file1");',
13
+ 'file2.css': 'body { margin: 0; }',
14
+ 'subdir': {
15
+ 'file3.jpg': '',
16
+ 'file4.png': ''
17
+ }
18
+ },
19
+ [`${process.cwd()}/${site}`]: {
20
+ 'myTheme': {
21
+ 'file.js': 'console.log("test");'
22
+ }
23
+ }
24
+ });
25
+ });
26
+
27
+ afterEach(() => {
28
+ mockFs.restore();
29
+ });
30
+
31
+ describe('listFilesRecursive', () => {
32
+ it('should list all files recursively', () => {
33
+ // Act
34
+ const result = fileManager.listFilesRecursive('/testDir');
35
+
36
+ // Assert
37
+ expect(result).toEqual([
38
+ '/testDir/file1.js',
39
+ '/testDir/file2.css',
40
+ '/testDir/subdir/file3.jpg',
41
+ '/testDir/subdir/file4.png'
42
+ ]);
43
+ });
44
+ });
45
+
46
+ describe('getRelativePath', () => {
47
+ it('should get the relative path of a file', () => {
48
+ // Arrange
49
+ const theme = 'myTheme';
50
+ const file = path.join(process.cwd(), site, theme, 'file.js');
51
+ const expectedRelativePath = 'file.js';
52
+
53
+ // Act
54
+ const result = fileManager.getRelativePath(theme, file);
55
+
56
+ // Assert
57
+ expect(result).toBe(expectedRelativePath);
58
+ });
59
+ });
60
+
61
+ describe('readFile', () => {
62
+ it('should read the contents of a file', () => {
63
+ // Arrange
64
+ const file = '/testDir/file1.js';
65
+ const fileContents = 'console.log("file1");';
66
+
67
+ // Act
68
+ const result = fileManager.readFile(file);
69
+
70
+ // Assert
71
+ expect(result).toBe(fileContents);
72
+ });
73
+ });
74
+
75
+ describe('isAllowedExtension', () => {
76
+ it('should return true for allowed extensions', () => {
77
+ // Arrange
78
+ const allowedFile = 'file.js';
79
+ const notAllowedFile = 'file.exe';
80
+
81
+ // Act
82
+ const isAllowedFileResult = fileManager.isAllowedExtension(allowedFile);
83
+ const isNotAllowedFileResult = fileManager.isAllowedExtension(notAllowedFile);
84
+
85
+ // Assert
86
+ expect(isAllowedFileResult).toBe(true);
87
+ expect(isNotAllowedFileResult).toBe(false);
88
+ });
89
+ });
90
+ });
@@ -1,17 +0,0 @@
1
- const Tsoft = require('../tsoft');
2
-
3
- async function save(site, theme) {
4
- const t = new Tsoft(site);
5
- const isValid = await t.isTokenValid(theme);
6
-
7
- if (isValid) {
8
- await t.saveOnlineStore(theme);
9
- console.log('Online store saved successfully.');
10
- } else {
11
- console.log('Invalid Bearer token. Please re-authenticate.');
12
- }
13
- }
14
-
15
- module.exports = {
16
- save
17
- };