@newlogic-digital/cli 0.2.0 → 1.0.1

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/index.mjs CHANGED
@@ -23,10 +23,13 @@ if (!command) {
23
23
  ${pc.green('newlogic init cms')} ${pc.yellow('<directory>')} - Creates a new ${pc.blue('@newlogic-digital/cms')} project in new directory with the name ${pc.yellow('<directory>')}
24
24
 
25
25
  -- cms --
26
- ${pc.green('newlogic cms prepare')} - Copies templates and sections from ${pc.blue('@newlogic-digital/ui')} project to ${pc.blue('@newlogic-digital/cms')}
27
- ${pc.green('newlogic cms prepare templates')} - Copies templates from ${pc.blue('@newlogic-digital/ui')} project to ${pc.blue('@newlogic-digital/cms')} even if they already exists
28
- ${pc.green('newlogic cms prepare sections')} - Copies sections from ${pc.blue('@newlogic-digital/ui')} project to ${pc.blue('@newlogic-digital/cms')} even if they already exists
29
- ${pc.green('newlogic cms new-section')} ${pc.yellow('<name>')} - Creates a new ${pc.blue('@newlogic-digital/cms')} section with name ${pc.yellow('<name>')}
26
+ ${pc.green('newlogic cms prepare')} - Copies templates and components from ${pc.blue('@newlogic-digital/ui')} project to ${pc.blue('@newlogic-digital/cms')}
27
+ ${pc.green('newlogic cms prepare views')} - Copies views from ${pc.blue('@newlogic-digital/ui')} project to ${pc.blue('@newlogic-digital/cms')} even if they already exists
28
+ ${pc.green('newlogic cms prepare components')} - Copies components from ${pc.blue('@newlogic-digital/ui')} project to ${pc.blue('@newlogic-digital/cms')} even if they already exists
29
+ ${pc.green('newlogic cms new-component')} ${pc.yellow('<name>')} - Creates a new ${pc.blue('@newlogic-digital/cms')} section with name ${pc.yellow('<name>')}
30
+ ${pc.red('newlogic cms prepare templates')} - (deprecated) Copies templates from ${pc.blue('@newlogic-digital/ui')} project to ${pc.blue('@newlogic-digital/cms')} even if they already exists
31
+ ${pc.red('newlogic cms prepare sections')} - (deprecated) Copies sections from ${pc.blue('@newlogic-digital/ui')} project to ${pc.blue('@newlogic-digital/cms')} even if they already exists
32
+ ${pc.red('newlogic cms new-section')} ${pc.yellow('<name>')} - (deprecated) Creates a new ${pc.blue('@newlogic-digital/cms')} section with name ${pc.yellow('<name>')}
30
33
  `)
31
34
  }
32
35
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newlogic-digital/cli",
3
- "version": "0.2.0",
3
+ "version": "1.0.1",
4
4
  "main": "index.mjs",
5
5
  "bin": {
6
6
  "newlogic-cli": "index.mjs",
@@ -13,7 +13,7 @@
13
13
  "lodash": "^4.17.21",
14
14
  "prompts": "^2.4.2",
15
15
  "fs-extra": "^11.2.0",
16
- "picocolors": "^1.0.0",
16
+ "picocolors": "^1.1.1",
17
17
  "fast-glob": "^3.3.2",
18
18
  "dedent": "^1.5.3"
19
19
  },
@@ -8,11 +8,15 @@ export const options = {
8
8
  path: {
9
9
  src: {
10
10
  templates: resolve(process.cwd(), 'src/templates'),
11
+ components: resolve(process.cwd(), 'src/templates/components'),
11
12
  sections: resolve(process.cwd(), 'src/templates/Sections'),
12
- views: resolve(process.cwd(), 'src/views')
13
+ views: resolve(process.cwd(), 'src/views'),
14
+ pages: resolve(process.cwd(), 'src/pages')
13
15
  },
14
16
  app: {
15
17
  templates: resolve(process.cwd(), 'app/Templates'),
18
+ components: resolve(process.cwd(), 'app/Components'),
19
+ componentsFactory: resolve(process.cwd(), 'app/Components/Factory'),
16
20
  sections: resolve(process.cwd(), 'app/Sections'),
17
21
  sectionsFactory: resolve(process.cwd(), 'app/Sections/Factory'),
18
22
  tempCache: resolve(process.cwd(), 'temp/cache')
@@ -25,6 +29,24 @@ export default async function cms(action, name) {
25
29
  await prepare(options).copy(name)
26
30
  }
27
31
 
32
+ if (action === 'new-component') {
33
+ const nameFormatted = name.replaceAll('/', '')
34
+
35
+ if (fs.existsSync(join(options.path.app.components, `${nameFormatted}.php`))) {
36
+ console.log(`${pc.red('✖')} section ${nameFormatted} already exists`)
37
+ process.exit(1)
38
+ }
39
+
40
+ prepare(options).writeComponent(name)
41
+ prepare(options).writeComponentFactory(nameFormatted)
42
+
43
+ if (fs.existsSync(options.path.app.tempCache)) {
44
+ fse.removeSync(options.path.app.tempCache)
45
+ }
46
+
47
+ console.log(`${pc.green('✔')} component ${nameFormatted} created`)
48
+ }
49
+
28
50
  if (action === 'new-section') {
29
51
  const nameFormatted = name.replaceAll('/', '')
30
52
 
@@ -1,5 +1,5 @@
1
1
  import FastGlob from 'fast-glob'
2
- import { join, relative, extname } from 'path'
2
+ import { join, relative, extname, basename } from 'path'
3
3
  import fs from 'fs'
4
4
  import fse from 'fs-extra'
5
5
  import pc from 'picocolors'
@@ -13,7 +13,25 @@ let templatesCount = 0
13
13
  export default function prepare(options) {
14
14
  return {
15
15
  async copy(name) {
16
- if (!name || name === 'sections') {
16
+ if (!name || name === 'components') {
17
+ if (!fs.existsSync(options.path.src.components)) {
18
+ console.log(`${pc.red('✖')} path ${options.path.src.components} doesn't exists`)
19
+ process.exit(1)
20
+ }
21
+
22
+ if (!fs.existsSync(options.path.app.components)) {
23
+ console.log(`${pc.red('✖')} path ${options.path.app.components} doesn't exists`)
24
+ process.exit(1)
25
+ }
26
+
27
+ if (!fs.existsSync(options.path.app.componentsFactory)) {
28
+ fs.mkdirSync(options.path.app.componentsFactory)
29
+ }
30
+
31
+ await this.copyComponents(name === 'components')
32
+ }
33
+
34
+ if (name === 'sections') {
17
35
  if (!fs.existsSync(options.path.src.sections)) {
18
36
  console.log(`${pc.red('✖')} path ${options.path.src.sections} doesn't exists`)
19
37
  process.exit(1)
@@ -31,7 +49,20 @@ export default function prepare(options) {
31
49
  await this.copySections(name === 'sections')
32
50
  }
33
51
 
34
- if (!name || name === 'templates') {
52
+ if (!name || name === 'views') {
53
+ if (!fs.existsSync(options.path.src.templates)) {
54
+ console.log(`${pc.red('✖')} path ${options.path.src.templates} doesn't exists`)
55
+ process.exit(1)
56
+ }
57
+
58
+ if (!fs.existsSync(options.path.app.components)) {
59
+ fs.mkdirSync(options.path.app.components)
60
+ }
61
+
62
+ await this.copyViews(name === 'views')
63
+ }
64
+
65
+ if (name === 'templates') {
35
66
  if (!fs.existsSync(options.path.src.templates)) {
36
67
  console.log(`${pc.red('✖')} path ${options.path.src.templates} doesn't exists`)
37
68
  process.exit(1)
@@ -123,7 +154,7 @@ export default function prepare(options) {
123
154
  annotations.push(`
124
155
  #[FieldType(FieldType::${getField(control, type)})]
125
156
  #[FieldName('${getTitle(control)}')]
126
- public ${phpType} $${control}${type === 'string' ? " = '" + value + "'" : ''};
157
+ public ?${phpType} $${control}${type === 'string' ? " = '" + value + "'" : ' = null'};
127
158
  `)
128
159
 
129
160
  if (type === 'object') {
@@ -199,6 +230,173 @@ export default function prepare(options) {
199
230
  }
200
231
  }
201
232
  },
233
+ writeComponent(name, path, force) {
234
+ if (!path) {
235
+ path = name + '.latte'
236
+ name = name.replaceAll('/', '')
237
+ }
238
+
239
+ if (!name.match(/(Ui)/)) {
240
+ if (!fs.existsSync(join(options.path.app.components, `${name}.php`)) || force) {
241
+ const schema = this.getComponentsSchema()
242
+ const controls = schema[path] ?? {}
243
+ let annotations = []
244
+ let objects = []
245
+
246
+ const getField = (control, type) => {
247
+ if (type === 'number') {
248
+ return 'INT'
249
+ }
250
+
251
+ if (control.startsWith('table')) {
252
+ return 'TABLE'
253
+ }
254
+
255
+ if (control === 'heading' || control === 'title') {
256
+ return 'TEXT'
257
+ }
258
+
259
+ if (control === 'content') {
260
+ return 'WSW'
261
+ }
262
+
263
+ if (type === 'string') {
264
+ return 'TEXTAREA'
265
+ }
266
+
267
+ if (type === 'boolean') {
268
+ return 'BOOLEAN'
269
+ }
270
+
271
+ return 'OTHER'
272
+ }
273
+
274
+ const getTitle = (control) => {
275
+ if (control === 'heading') {
276
+ return 'Nadpis'
277
+ }
278
+
279
+ if (control === 'title') {
280
+ return 'Titulek'
281
+ }
282
+
283
+ if (control === 'content') {
284
+ return 'Obsah'
285
+ }
286
+
287
+ if (control === 'text') {
288
+ return 'Text'
289
+ }
290
+
291
+ return control[0].toUpperCase() + control.slice(1)
292
+ }
293
+
294
+ Object.keys(controls).forEach(control => {
295
+ if (control !== 'src') {
296
+ const type = typeof controls[control]
297
+ const value = typeof controls[control] === 'object' ? JSON.stringify(controls[control]) : controls[control]
298
+
299
+ let phpType = type
300
+
301
+ if (type === 'object' && Array.isArray(controls[control])) {
302
+ phpType = 'array'
303
+ } else if (type === 'boolean') {
304
+ phpType = 'bool'
305
+ } else if (type === 'number') {
306
+ phpType = 'int'
307
+ }
308
+
309
+ annotations.push(`
310
+ #[FieldType(FieldType::${getField(control, type)})]
311
+ #[FieldName('${getTitle(control)}')]
312
+ public ?${phpType} $${control}${type === 'string' ? " = '" + value + "'" : ' = null'};
313
+ `)
314
+
315
+ if (type === 'object') {
316
+ objects.push(`
317
+ $this->${control} = json_decode('${value}', false);
318
+ `)
319
+ }
320
+ }
321
+ })
322
+
323
+ if (annotations.length > 0) {
324
+ annotations = annotations.map(item => item).join('')
325
+ } else {
326
+ annotations = ''
327
+ }
328
+
329
+ if (objects.length > 0) {
330
+ objects = objects.map(item => item).join('')
331
+ } else {
332
+ objects = ''
333
+ }
334
+
335
+ fs.writeFileSync(join(options.path.app.components, `${name}.php`),
336
+ dedent`
337
+ <?php
338
+
339
+ declare(strict_types=1);
340
+
341
+ namespace App\Components;
342
+
343
+ use App\Components\Attributes\FieldName;
344
+ use App\Components\Attributes\FieldType;
345
+ use App\Components\Attributes\SectionName;
346
+
347
+ #[ComponentName('${name}')]
348
+ class ${name} extends BaseComponent
349
+ {
350
+ ${annotations}
351
+ public function render() : void
352
+ {
353
+ ${objects}
354
+ $this->template->render(TEMPLATES_DIR . '/${path}');
355
+ }
356
+ }
357
+ `
358
+ )
359
+
360
+ sectionsCount += 1
361
+ }
362
+ }
363
+ },
364
+ writeComponentFactory(name, force) {
365
+ if (!name.match(/(Ui)/)) {
366
+ if (!fs.existsSync(join(options.path.app.componentsFactory, `${name}Factory.php`)) || force) {
367
+ fs.writeFileSync(join(options.path.app.componentsFactory, `${name}Factory.php`),
368
+ dedent`
369
+ <?php
370
+
371
+ declare(strict_types=1);
372
+
373
+ namespace App\SComponents\Factory;
374
+
375
+ use ${'App\\Components\\'}${name};
376
+
377
+ interface ${name}Factory
378
+ {
379
+ public function create(): ${name};
380
+ }
381
+ `
382
+ )
383
+
384
+ sectionsFactoryCount += 1
385
+ }
386
+ }
387
+ },
388
+ async copyComponents(force = false) {
389
+ const components = FastGlob.sync(join(options.path.src.components, '/**'))
390
+
391
+ components.forEach(path => {
392
+ const name = basename(relative(options.path.src.components, path), extname(relative(options.path.src.components, path)))
393
+
394
+ this.writeComponent(name, relative(options.path.src.templates, path), force)
395
+ this.writeComponentFactory(name, force)
396
+ })
397
+
398
+ console.log(`${pc.green('✔')} ${sectionsCount} components and ${sectionsFactoryCount} factory files copied to app`)
399
+ },
202
400
  async copySections(force = false) {
203
401
  const sections = FastGlob.sync(join(options.path.src.sections, '/**'))
204
402
 
@@ -211,6 +409,18 @@ export default function prepare(options) {
211
409
 
212
410
  console.log(`${pc.green('✔')} ${sectionsCount} sections and ${sectionsFactoryCount} factory files copied to app`)
213
411
  },
412
+ async copyViews(force = false) {
413
+ const templates = FastGlob.sync(join(options.path.src.templates, '/**')).filter(entry => {
414
+ return !fs.existsSync(join(options.path.src.views, relative(options.path.src.templates, entry))) || force
415
+ })
416
+
417
+ for (const path of templates) {
418
+ await fse.copy(path, join(options.path.src.views, relative(options.path.src.templates, path)))
419
+ templatesCount += 1
420
+ }
421
+
422
+ console.log(`${pc.green('✔')} ${templatesCount} views files copied from templates`)
423
+ },
214
424
  async copyTemplates(force = false) {
215
425
  const templates = FastGlob.sync(join(options.path.src.templates, '/**')).filter(entry => {
216
426
  return !fs.existsSync(join(options.path.app.templates, relative(options.path.src.templates, entry))) || force
@@ -254,6 +464,39 @@ export default function prepare(options) {
254
464
  sectionsSchema[sortedSection.src] = sortedSection
255
465
  })
256
466
 
467
+ return sectionsSchema
468
+ },
469
+ getComponentsSchema() {
470
+ const views = FastGlob.sync(join(options.path.src.pages, '/**/*.json'))
471
+ const sectionsUnsorted = {}
472
+ const sectionsSchema = {}
473
+
474
+ views.forEach(path => {
475
+ const json = JSON.parse(fs.readFileSync(path).toString())
476
+ const sections = [...(json.body ?? []), ...(json.head ?? []), ...(json.foot ?? [])]
477
+
478
+ sections.forEach((section) => {
479
+ if (section.src) {
480
+ if (typeof sectionsUnsorted[section.src] === 'undefined') {
481
+ sectionsUnsorted[section.src] = []
482
+ sectionsUnsorted[section.src].push(section)
483
+ } else {
484
+ sectionsUnsorted[section.src].push(section)
485
+ }
486
+ }
487
+ })
488
+ })
489
+
490
+ Object.keys(sectionsUnsorted).forEach(key => {
491
+ const sortedSection = {}
492
+
493
+ sectionsUnsorted[key].forEach(section => {
494
+ lodash.merge(sortedSection, section)
495
+ })
496
+
497
+ sectionsSchema[sortedSection.src] = sortedSection
498
+ })
499
+
257
500
  return sectionsSchema
258
501
  }
259
502
  }
@@ -76,16 +76,18 @@ export default async function cms(name, { variant, branch }) {
76
76
  await move('app')
77
77
  await move('config')
78
78
  await move('log')
79
- await move('public/.htaccess')
80
79
  await move('public/index.php')
80
+ await move('src/views')
81
81
  await move('temp')
82
+ await move('tests')
82
83
  await move('.gitignore', { overwrite: true })
83
- await move('.htaccess')
84
- await move('Makefile')
84
+ await move('README.md', { overwrite: true })
85
85
  await move('composer.json')
86
86
  await move('composer.lock')
87
87
  await move('docker-compose.yml')
88
+ await move('Makefile')
88
89
  await move('phpstan.neon')
90
+ await move('phpunit.xml')
89
91
  await move('pint.json')
90
92
  await move('rector.php')
91
93
  await fse.move(join(tempDir, '.gitlab-ci.prod.yml'), resolve(process.cwd(), '.gitlab-ci.yml'), { overwrite: true }).catch(err => console.log(`${pc.red(err)} - .gitlab-ci.yml`))