frontfire 0.5.0 → 0.8.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/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "frontfire",
3
- "version": "0.5.0",
3
+ "version": "0.8.0",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
7
7
  "scripts": {
8
8
  "start": "node src/index.js",
9
+ "start -version": "node src/index.js version",
10
+ "create in test": "node src/index.js create \"Hello World\"",
9
11
  "test": "echo \"Error: no test specified\" && exit 1"
10
12
  },
11
13
  "repository": {
@@ -23,6 +25,7 @@
23
25
  "chalk": "^5.2.0",
24
26
  "commander": "^10.0.0",
25
27
  "conf": "^11.0.1",
28
+ "download-git-repo": "^3.0.2",
26
29
  "ejs": "^3.1.10",
27
30
  "esbuild": "^0.19.5",
28
31
  "esbuild-copy-static-files": "^0.1.0",
@@ -31,7 +34,8 @@
31
34
  "lodash": "^4.17.21",
32
35
  "luxon": "^3.5.0",
33
36
  "php-server": "^1.0.0",
34
- "prettier": "^2.8.4"
37
+ "prettier": "^2.8.4",
38
+ "slugify": "^1.6.6"
35
39
  },
36
40
  "bin": {
37
41
  "frontfire": "src/index.js"
@@ -56,8 +56,8 @@ const entryPoints = [
56
56
  path.resolve( './src/app/main.js' ),
57
57
  path.resolve( './src/app/main.css' )
58
58
  */
59
- `.${path.sep}src${path.sep}app${path.sep}main.js`,
60
- `.${path.sep}src${path.sep}app${path.sep}main.css`
59
+ `./src/app/main.js`,
60
+ `./src/app/main.css`
61
61
  ];
62
62
 
63
63
  const outDirDebug = `${buildDir}/debug/app/`
@@ -66,16 +66,6 @@ const outDirRelease = `${buildDir}/release/app`;
66
66
  const staticAssetsDestDebug = `${buildDir}/debug/assets`;
67
67
  const staticAssetsDestRelease = `${buildDir}/release/assets`;
68
68
 
69
- /*
70
- console.log( "Build folder:" );
71
- console.log( buildDir );
72
-
73
-
74
- console.log( "Release folder:" );
75
- console.log( outDirRelease );
76
-
77
- console.log( "First entry point", entryPoints[ 0 ] );
78
- */
79
69
  export default {
80
70
  "buildDir" : buildDir,
81
71
  "debug" : {
package/src/FrontFire.js CHANGED
@@ -127,7 +127,7 @@ export default async function frontFire( isWatch, cfg = {} )
127
127
 
128
128
  // CACHEBREAK
129
129
  let indexContent = fs.readFileSync( `${rootBuildDir}${path.sep}release${path.sep}index.html`, { encoding: 'utf8', flag: 'r' } );
130
- const changedContent = indexContent.replace( /IFJSCACHEBREAK/g, (DateTime.now()).valueOf() );
130
+ const changedContent = indexContent.replace( /INFRONTCACHEBREAK/g, (DateTime.now()).valueOf() );
131
131
  fs.writeFileSync( `${rootBuildDir}${path.sep}release${path.sep}index.html`, changedContent );
132
132
  }
133
133
  };
package/src/index.js CHANGED
@@ -1,13 +1,19 @@
1
1
  #! /usr/bin/env node
2
2
 
3
3
  import fs from "node:fs";
4
+ import { readdir, readFile, writeFile, unlink } from 'node:fs/promises';
4
5
  import ejs from "ejs";
5
6
  import chalk from "chalk";
7
+ import download from "download-git-repo";
8
+ import { promisify } from "util";
6
9
 
7
10
  import _ from "lodash";
8
11
  import prettier from "prettier";
9
12
  import { program } from "commander";
10
13
  import path from "node:path";
14
+ import { createInterface } from "readline";
15
+ import { stdin as input, stdout as output } from "node:process";
16
+ import slugify from "slugify";
11
17
 
12
18
  import frontFire from "./FrontFire.js";
13
19
  import defaultConfig from "./DefaultConfig.js";
@@ -17,6 +23,16 @@ import wcTemplate from "./templates/wc-template.html.js";
17
23
  import stateClass from "./templates/state-class.js";
18
24
  import stateTemplate from "./templates/state-template.html.js";
19
25
  import poIndex from "./templates/po-index.js";
26
+ import dictTemplate from "./templates/dictionary.js";
27
+
28
+ async function askYesNo(question) {
29
+ const rl = createInterface({ input, output });
30
+ const answer = await new Promise((resolve) => {
31
+ rl.question(question, (ans) => resolve(ans.trim()));
32
+ });
33
+ rl.close();
34
+ return /^(y|yes)$/i.test(answer);
35
+ }
20
36
 
21
37
  async function performInit()
22
38
  {
@@ -36,28 +52,71 @@ async function performInit()
36
52
  }
37
53
  )
38
54
  );
55
+
56
+ if ( false === fs.existsSync( './src/assets' ) )
57
+ {
58
+ fs.mkdirSync( './src/assets' );
59
+ }
39
60
  }
40
61
 
41
- async function createInfrontJsStarter( appName = null )
62
+ async function createInfrontJsStarter( appName, appPath )
42
63
  {
43
- // Deep Copy
64
+ // Callback → Promise
65
+ const downloadAsync = promisify(download);
66
+
67
+ async function downloadRepo(repo, targetDir) {
68
+ try {
69
+ await downloadAsync(repo, targetDir, { clone: false });
70
+ console.log(`✅ Repo erfolgreich geladen nach ${targetDir}`);
71
+ } catch (err) {
72
+ console.error("❌ Fehler beim Laden des Repos:", err);
73
+ }
74
+ }
75
+
76
+ async function updatePackageName(newName, packagePath = "./package.json") {
77
+ try {
78
+ const fullPath = path.resolve(packagePath);
79
+
80
+ // read package.json
81
+ const data = await readFile(fullPath, "utf-8");
82
+ const pkg = JSON.parse(data);
83
+
84
+ // replace if it matches the source name
85
+ if (pkg.name === "infrontjs-starter") {
86
+ pkg.name = newName;
87
+
88
+ await writeFile(fullPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
89
+ console.log(`✅ Updated package.json name to "${newName}"`);
90
+ } else {
91
+ console.log(`ℹ️ package.json name is not "infrontjs-starter" (found "${pkg.name}"), no change made.`);
92
+ }
93
+ } catch (err) {
94
+ console.error("❌ Failed to update package.json:", err);
95
+ }
96
+ }
97
+
98
+
99
+ // Deep Copy
44
100
  const initConfig = JSON.parse( JSON.stringify( defaultConfig ) );
45
101
 
46
102
  _.unset( initConfig, 'debug.esbuild.plugins' );
47
103
  _.unset( initConfig, 'release.esbuild.plugins' );
48
104
 
49
- if ( null === appName || appName.length === 0 )
50
- {
51
- appName = 'InfrontJS Starter';
52
- }
105
+ await downloadRepo("github:infrontjs/starter", appPath );
106
+ await updatePackageName( appName, `${appPath}/package.json` );
107
+ await unlink( `${appPath}/LICENSE` );
53
108
 
54
- const srcFolder = path.resolve( '.' ) + path.separator + 'src-to-test';
109
+ await writeFile( `${appPath}/frontfire.json`, JSON.stringify(initConfig, null, 2) + "\n", "utf8");
110
+
111
+ console.log(chalk.green.bold(`\nInfrontJS project "${appName}" successfully created at ${appPath}\n`));
112
+
113
+ console.log(chalk.cyan.bold("Next steps:"));
114
+ console.log(` ${chalk.yellow("1.")} Change directory to ${chalk.magenta(path.resolve(appPath))}`);
115
+ console.log(` ${chalk.yellow("2.")} Modify ${chalk.magenta(path.join(appPath, "frontfire.config"))} and ${chalk.magenta(path.join(appPath, "package.json"))} to your needs`);
116
+ console.log(` ${chalk.yellow("3.")} Run ${chalk.blueBright("npm install")}`);
117
+ console.log(` ${chalk.yellow("4.")} Run ${chalk.blueBright("frontfire start-dev")} to start development`);
118
+ console.log(` ${chalk.yellow("5.")} Alternatively run ${chalk.blueBright("frontfire build")} to build your minified project before deployment\n`);
55
119
 
56
- if ( true === fs.existsSync( srcFolder ) )
57
- {
58
- console.warn( `Sourcefolder ${srcFolder} already exists!` );
59
- return;
60
- }
61
120
  }
62
121
 
63
122
  async function generatesPathObject( name )
@@ -118,7 +177,7 @@ async function generatesState( name, options )
118
177
 
119
178
  console.log( chalk.green.bold( `State ${stateName} successfully generated.`) );
120
179
  console.log( chalk.italic( 'Dont forget to add the state to your states instance, e.g.' ) );
121
- console.log( chalk.italic.bgWhite( `myApp.states.add( ${stateName} );`) );
180
+ console.log( chalk.italic.bgWhite( `myApp.states.add( ${stateName}State);`) );
122
181
  }
123
182
 
124
183
  async function generatesWebComponent( name = null )
@@ -143,7 +202,7 @@ async function generatesWebComponent( name = null )
143
202
  wcName = name.replace(/[A-Z]/g, (match, offset) => (offset > 0 ? '-' : '') + match.toLowerCase());
144
203
  fileName = fileName.charAt(0).toLowerCase() + fileName.slice(1);
145
204
 
146
- const srcFolder = path.resolve( '.' ) + path.sep + fileName;
205
+ const srcFolder = path.resolve( '.' ) + path.sep + wcName;
147
206
  if ( true === fs.existsSync( srcFolder ) )
148
207
  {
149
208
  console.error( `Folder "${srcFolder}" already exists!` );
@@ -156,13 +215,115 @@ async function generatesWebComponent( name = null )
156
215
  ejs.render( wcIndex, { wcName: wcName, className: className } )
157
216
  );
158
217
  fs.writeFileSync(
159
- srcFolder + path.sep + 'template.js',
218
+ srcFolder + path.sep + 'template.html',
160
219
  ejs.render( wcTemplate, { wcName: wcName } )
161
220
  );
162
221
 
163
222
  console.log( chalk.green.bold( `Web component ${wcName} successfully created.` ) );
164
223
  }
165
224
 
225
+ async function generateDictionary( pathToDictionary, options )
226
+ {
227
+ const defaultLang = 'en';
228
+ const countryCodes = options.countrycodes.split( "," );
229
+ const defaultCountryCode = options.defaulcountrycode;
230
+ const rootFolder = path.resolve( options.rootpath );
231
+ const newDict = [];
232
+ const lKeys = [];
233
+ let currentDict = {};
234
+
235
+ const indexOfDefaultLang = countryCodes.indexOf( defaultLang );
236
+ if ( -1 < indexOfDefaultLang )
237
+ {
238
+ countryCodes.splice( indexOfDefaultLang, 1 );
239
+ }
240
+
241
+ try
242
+ {
243
+ if ( fs.existsSync( pathToDictionary ) )
244
+ {
245
+ let dictContent = fs.readFileSync( pathToDictionary, { encoding: 'utf8' } );
246
+ dictContent = dictContent.replace( "export default", "" );
247
+ dictContent = dictContent.trim();
248
+ dictContent = dictContent.replace( new RegExp(';$', 'gm'), "" );
249
+ try {
250
+ currentDict = JSON.parse( dictContent );
251
+ } catch( e ) {
252
+ console.error( `Cannot parse current dictionary.`, e );
253
+ currentDict = {};
254
+ }
255
+ }
256
+
257
+ const files = await readdir( rootFolder, { recursive : true } );
258
+ for ( let fi = 0; fi < files.length; fi++ )
259
+ {
260
+ const file = files[ fi ];
261
+ if ( false === fs.lstatSync( file ).isFile() )
262
+ {
263
+ continue;
264
+ }
265
+
266
+ const regex = /_lcs\(\s*?[\'|\"|\`](.+)[\'|\"|`]\s*?\)/gm;
267
+ if ( -1 < [ '.html', '.js' ].indexOf( ( '.' + file.split( '.' ).pop() ) ) )
268
+ {
269
+ const str = await readFile( file, 'utf8' );
270
+
271
+ let m;
272
+ while ((m = regex.exec(str)) !== null) {
273
+
274
+ // This is necessary to avoid infinite loops with zero-width matches
275
+ if (m.index === regex.lastIndex) {
276
+ regex.lastIndex++;
277
+ }
278
+
279
+ // The result can be accessed through the `m`-variable.
280
+ m.forEach((match, groupIndex) => {
281
+
282
+ if ( false === match.includes( '_lcs' ) )
283
+ {
284
+ if ( -1 === lKeys.indexOf( match ) )
285
+ {
286
+ lKeys.push( match );
287
+ let newEntry = { "key" : match, trans : [] };
288
+ if ( currentDict.hasOwnProperty( match ) && currentDict[ match ].hasOwnProperty( defaultCountryCode ) && currentDict[ match ][ defaultCountryCode ] !== null )
289
+ {
290
+ newEntry.trans.push( { "cc" : defaultCountryCode, "val" : currentDict[ match ][ defaultCountryCode ] } );
291
+ }
292
+ else
293
+ {
294
+ newEntry.trans.push( { "cc" : defaultCountryCode, "val" : match } );
295
+ }
296
+
297
+ for ( let ci = 0; ci < countryCodes.length; ci++ )
298
+ {
299
+ if ( currentDict.hasOwnProperty( match ) && currentDict[ match ].hasOwnProperty( countryCodes[ ci ] ) && currentDict[ match ][ countryCodes[ ci ] ] !== null )
300
+ {
301
+ newEntry.trans.push( { "cc" : countryCodes[ ci ], "val" : currentDict[ match ][ countryCodes[ ci ] ] } );
302
+ }
303
+ else
304
+ {
305
+ newEntry.trans.push( { "cc" : countryCodes[ ci ], "val" : null } );
306
+ }
307
+ }
308
+ newDict.push( newEntry );
309
+ }
310
+ }
311
+ });
312
+ }
313
+ }
314
+ }
315
+
316
+ const newDictFileContent = ejs.render( dictTemplate, { newDict: newDict } );
317
+ fs.writeFileSync( pathToDictionary, newDictFileContent, { encoding: 'utf8' } );
318
+
319
+ console.log( chalk.green.bold( `Dictionary successfully created under ${pathToDictionary}. Total keys: ${newDict.length}.` ) );
320
+ }
321
+ catch( e )
322
+ {
323
+ console.error( e );
324
+ }
325
+ }
326
+
166
327
  // Try to load custom config
167
328
  let customConfig = null;
168
329
 
@@ -194,9 +355,38 @@ program
194
355
  .action( function() { performInit(); } );
195
356
 
196
357
  program
197
- .command( 'create' )
198
- .description( 'Creates starter InfrontJS application.' )
199
- .action( function() { createInfrontJsStarter(); } );
358
+ .command("create")
359
+ .description("Create a new InfrontJS project")
360
+ .argument("[appName]", "Application name", "InfrontJS Starter")
361
+ .argument("[appPath]", "Target directory")
362
+ .action(async ( appName, appPath) => {
363
+ const finalName = appName ?? "InfrontJS Starter";
364
+ if ( !appPath )
365
+ {
366
+ appPath = processY.cwd();
367
+ appPath = path.join( appPath, slugify( finalName, { lower: true , strict: true } ) );
368
+ }
369
+
370
+ const resolvedPath = path.resolve(appPath ?? process.cwd());
371
+
372
+ console.log(chalk.cyan(`\nAbout to create:`));
373
+ console.log(` ${chalk.bold("Name:")} ${finalName}`);
374
+ console.log(` ${chalk.bold("Path:")} ${resolvedPath}\n`);
375
+
376
+ const confirmed = await askYesNo(
377
+ chalk.yellow("Do you really want to create the project in this directory? (y/N) ")
378
+ );
379
+
380
+ if (!confirmed) {
381
+ console.log(chalk.red("Aborted."));
382
+ process.exitCode = 1;
383
+ return;
384
+ }
385
+
386
+ console.log(chalk.green("Starting..."));
387
+ await createInfrontJsStarter( finalName, resolvedPath );
388
+ console.log(chalk.green("Done."));
389
+ });
200
390
 
201
391
  program
202
392
  .command( 'gwc' )
@@ -204,6 +394,15 @@ program
204
394
  .description( 'Generates a webcomponent with specific name.' )
205
395
  .action( function( name ) { generatesWebComponent( name ); } );
206
396
 
397
+ program
398
+ .command( 'gd' )
399
+ .argument( '<pathToDictionary>', 'Path to dictionary file. If it exists, it gets overwritten.' )
400
+ .option( '-cc, --countrycodes <ccodes>', 'Comma seperated list of country codes.', 'en,de' )
401
+ .option( '-dcc, --defaulcountrycode <ccode>', 'Default country code', 'en' )
402
+ .option( '-rp, --rootpath <rootpath>', 'Root path to parse files for translations.', './' )
403
+ .description( 'Generates a dictionary file.' )
404
+ .action( function( pathToDictionary, options ) { generateDictionary( pathToDictionary, options ); } );
405
+
207
406
  program
208
407
  .command( 'gs' )
209
408
  .argument( '<name>', 'Name of state.' )
@@ -222,5 +421,10 @@ program
222
421
  .description( 'Building for production.' )
223
422
  .action( function() { frontFire( false, customConfig ); } );
224
423
 
424
+ program
425
+ .command( 'version' )
426
+ .description( 'Prints the version number.' )
427
+ .action( function() { console.log( '0.8.0' ); } );
428
+
225
429
 
226
430
  program.parse();
@@ -0,0 +1,16 @@
1
+ export default 'export default {\n'+
2
+ '<% for ( let ki = 0; ki < newDict.length; ki++ ) { %>\n' +
3
+ ' "<%= newDict[ ki ].key %>" : {\n' +
4
+ '<% for( let ti = 0; ti < newDict[ ki ].trans.length; ti++ ) { %>' +
5
+ '<% if ( newDict[ ki ].trans[ ti ].val === null ) { %>' +
6
+ ' "<%= newDict[ ki ].trans[ ti ].cc; %>" : null<%= ((ti+1) < newDict[ ki ].trans.length) ? "," : "" %>' +
7
+ '<% } else { %>' +
8
+ ' "<%= newDict[ ki ].trans[ ti ].cc; %>" : "<%= newDict[ ki ].trans[ ti ].val; %>"<%= ((ti+1) < newDict[ ki ].trans.length) ? "," : "" %>' +
9
+ '<% } %>' +
10
+ '<% if ((ti+1) < newDict[ ki ].trans.length) { %> ' +
11
+ '\n' +
12
+ '<% } %>' +
13
+ '<% } %>\n' +
14
+ ' }<%= ((ki+1) < newDict.length) ? "," : "" %>\n' +
15
+ '<% } %>\n' +
16
+ '};';
@@ -1,6 +1,6 @@
1
- export default 'import { BaseState } from "infrontjs";\n' +
1
+ export default 'import { State } from "infrontjs";\n' +
2
2
  'import template from \'./<%= stateName %>Template.html\';\n' +
3
- 'export class <%= stateName %> extends BaseState\n' +
3
+ 'export class <%= stateName %>State extends State\n' +
4
4
  '{\n' +
5
5
  ' static ROUTE = "/<%= stateId %>";\n' +
6
6
  ' static ID = "<%= stateId %>";\n' +
@@ -29,4 +29,4 @@ export default '' +
29
29
  ' }\n' +
30
30
  '}\n' +
31
31
  '\n' +
32
- 'customElements.define(<%= wcName %>, <%= className %> );'
32
+ 'customElements.define( "<%= wcName %>", <%= className %> );'