frontfire 0.3.3 → 0.5.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/README.md CHANGED
@@ -6,11 +6,11 @@ It is entirely built upon the great esbuild ecosystem.
6
6
 
7
7
  ## Logic
8
8
 
9
- Structure
9
+ Required structure of an InfrontJS project is as follows
10
10
 
11
11
  - src/assets/**/*
12
12
  - src/app/main.js
13
- - src/app/app.css
13
+ - src/app/main.css
14
14
  - src/index.html
15
15
 
16
- Entrypoint for building is main.js and app.css
16
+ Entrypoint for building is main.js and main.css
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontfire",
3
- "version": "0.3.3",
3
+ "version": "0.5.0",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -23,11 +23,13 @@
23
23
  "chalk": "^5.2.0",
24
24
  "commander": "^10.0.0",
25
25
  "conf": "^11.0.1",
26
+ "ejs": "^3.1.10",
26
27
  "esbuild": "^0.19.5",
27
28
  "esbuild-copy-static-files": "^0.1.0",
28
29
  "esbuild-plugin-copy": "^2.0.2",
29
30
  "fs-extra": "^11.1.1",
30
31
  "lodash": "^4.17.21",
32
+ "luxon": "^3.5.0",
31
33
  "php-server": "^1.0.0",
32
34
  "prettier": "^2.8.4"
33
35
  },
@@ -6,8 +6,33 @@ import path from "node:path";
6
6
  import { copy } from "esbuild-plugin-copy";
7
7
  import copyStaticFiles from "esbuild-copy-static-files";
8
8
 
9
+ let currentDir = path.dirname( '.' );
10
+ let rootDir = null;
11
+ const maxCount = 10;
12
+ let currentCount = 0;
13
+ while ( null === rootDir )
14
+ {
15
+ if ( fs.existsSync( currentDir + path.sep + 'package.json' ) )
16
+ {
17
+ rootDir = currentDir;
18
+ }
19
+ else
20
+ {
21
+ currentDir += '/..';
22
+ currentCount++;
23
+ if ( currentCount > maxCount ) {
24
+ break;
25
+ }
26
+ }
27
+ }
9
28
 
10
- const rootFiles = fs.readdirSync( `src${path.sep}`, { withFileTypes : true } );
29
+ if ( rootDir === null )
30
+ {
31
+ console.error( 'Too many recursions. Root directory not found.' );
32
+ exit;
33
+ }
34
+
35
+ const rootFiles = fs.readdirSync( `${rootDir}${path.sep}src${path.sep}`, { withFileTypes : true } );
11
36
  const rootFilesToCopy = [];
12
37
  for ( let ri = 0; ri < rootFiles.length; ri++ )
13
38
  {
@@ -25,21 +50,19 @@ const buildDir = `build`;
25
50
  const entryPoints = [
26
51
  /*
27
52
  "src/app/main.js",
28
- "src/app/app.css"
53
+ "src/app/main.css"
29
54
  */
30
55
  /*
31
56
  path.resolve( './src/app/main.js' ),
32
- path.resolve( './src/app/app.css' )
57
+ path.resolve( './src/app/main.css' )
33
58
  */
34
59
  `.${path.sep}src${path.sep}app${path.sep}main.js`,
35
- `.${path.sep}src${path.sep}app${path.sep}app.css`
60
+ `.${path.sep}src${path.sep}app${path.sep}main.css`
36
61
  ];
37
62
 
38
63
  const outDirDebug = `${buildDir}/debug/app/`
39
64
  const outDirRelease = `${buildDir}/release/app`;
40
65
 
41
- console.log( outDirRelease );
42
-
43
66
  const staticAssetsDestDebug = `${buildDir}/debug/assets`;
44
67
  const staticAssetsDestRelease = `${buildDir}/release/assets`;
45
68
 
package/src/FrontFire.js CHANGED
@@ -6,6 +6,8 @@ import fse from "fs-extra";
6
6
  import path from "node:path";
7
7
  import * as child from "child_process";
8
8
 
9
+ import { DateTime } from "luxon";
10
+
9
11
  import DEFAULT_CONFIG from "./DefaultConfig.js";
10
12
 
11
13
  export default async function frontFire( isWatch, cfg = {} )
@@ -42,10 +44,12 @@ export default async function frontFire( isWatch, cfg = {} )
42
44
  }
43
45
 
44
46
  let esbuildOpts = null;
47
+ let serverOpts = null;
45
48
 
46
49
  if ( true === isWatch )
47
50
  {
48
51
  esbuildOpts = _.get( config, 'debug.esbuild' );
52
+ serverOpts = _.get( config, 'debug.server' );
49
53
  }
50
54
  else
51
55
  {
@@ -56,6 +60,8 @@ export default async function frontFire( isWatch, cfg = {} )
56
60
 
57
61
  if ( true === isWatch )
58
62
  {
63
+ const outerPort = serverOpts && serverOpts.hasOwnProperty( 'port' ) ? +serverOpts.port : 3000;
64
+
59
65
  fs.watchFile( `src${path.sep}index.html`, async ( curr, prev ) =>
60
66
  {
61
67
  const result = await ctx.rebuild();
@@ -63,11 +69,14 @@ export default async function frontFire( isWatch, cfg = {} )
63
69
  await ctx.watch();
64
70
  let { host, port } = await ctx.serve(
65
71
  {
66
- servedir : `.${path.sep}${rootBuildDir}${path.sep}debug`
72
+ servedir : `.${path.sep}${rootBuildDir}${path.sep}debug`,
73
+ // @todo use param for index.html
74
+ fallback : `.${path.sep}${rootBuildDir}${path.sep}debug${path.sep}index.html`
75
+ //onRequest : function ( ) { console.log( "Hello" ); }
67
76
  }
68
77
  );
69
78
 
70
- // Then start a proxy server on port 3000
79
+ // Then start a proxy server
71
80
  http.createServer((req, res) => {
72
81
 
73
82
  const options = {
@@ -75,7 +84,7 @@ export default async function frontFire( isWatch, cfg = {} )
75
84
  port: port,
76
85
  path: req.url,
77
86
  method: req.method,
78
- headers: req.headers,
87
+ headers: req.headers
79
88
  };
80
89
 
81
90
  if ( 'php' === indexType && req.url === "/" && req.method.toLowerCase() === 'get' )
@@ -107,13 +116,18 @@ export default async function frontFire( isWatch, cfg = {} )
107
116
  // Forward the body of the request to esbuild
108
117
  req.pipe(proxyReq, { end: true })
109
118
 
110
- }).listen(3000);
119
+ }).listen( outerPort );
111
120
 
112
- console.log( `> InfrontJS:http://localhost:${port}` );
121
+ console.log( `> InfrontJS:http://localhost:${outerPort}` );
113
122
  }
114
123
  else
115
124
  {
116
125
  await ctx.rebuild();
117
126
  ctx.dispose();
127
+
128
+ // CACHEBREAK
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() );
131
+ fs.writeFileSync( `${rootBuildDir}${path.sep}release${path.sep}index.html`, changedContent );
118
132
  }
119
133
  };
package/src/index.js CHANGED
@@ -1,14 +1,23 @@
1
1
  #! /usr/bin/env node
2
2
 
3
3
  import fs from "node:fs";
4
+ import ejs from "ejs";
5
+ import chalk from "chalk";
4
6
 
5
7
  import _ from "lodash";
6
8
  import prettier from "prettier";
7
9
  import { program } from "commander";
10
+ import path from "node:path";
8
11
 
9
12
  import frontFire from "./FrontFire.js";
10
13
  import defaultConfig from "./DefaultConfig.js";
11
14
 
15
+ import wcIndex from "./templates/wc-index.js";
16
+ import wcTemplate from "./templates/wc-template.html.js";
17
+ import stateClass from "./templates/state-class.js";
18
+ import stateTemplate from "./templates/state-template.html.js";
19
+ import poIndex from "./templates/po-index.js";
20
+
12
21
  async function performInit()
13
22
  {
14
23
  // Deep Copy
@@ -27,8 +36,131 @@ async function performInit()
27
36
  }
28
37
  )
29
38
  );
39
+ }
40
+
41
+ async function createInfrontJsStarter( appName = null )
42
+ {
43
+ // Deep Copy
44
+ const initConfig = JSON.parse( JSON.stringify( defaultConfig ) );
45
+
46
+ _.unset( initConfig, 'debug.esbuild.plugins' );
47
+ _.unset( initConfig, 'release.esbuild.plugins' );
48
+
49
+ if ( null === appName || appName.length === 0 )
50
+ {
51
+ appName = 'InfrontJS Starter';
52
+ }
53
+
54
+ const srcFolder = path.resolve( '.' ) + path.separator + 'src-to-test';
55
+
56
+ if ( true === fs.existsSync( srcFolder ) )
57
+ {
58
+ console.warn( `Sourcefolder ${srcFolder} already exists!` );
59
+ return;
60
+ }
61
+ }
62
+
63
+ async function generatesPathObject( name )
64
+ {
65
+ let fileName = null;
66
+ let poName = null;
67
+
68
+ fileName = name.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
69
+ poName = fileName.charAt( 0 ).toUpperCase() + fileName.slice( 1 );
70
+
71
+ const poFilename = path.resolve( '.' ) + path.sep + poName + '.js';
72
+ if ( true === fs.existsSync( poFilename ) )
73
+ {
74
+ console.error( `File "${poFilename}" already exists!` );
75
+ return false;
76
+ }
77
+
78
+ fs.writeFileSync(
79
+ poFilename,
80
+ ejs.render( poIndex, { poName: poName } )
81
+ );
82
+
83
+ console.log( chalk.green.bold( `PathObject ${poName} successfully generated.`) );
84
+ }
85
+
86
+ async function generatesState( name, options )
87
+ {
88
+ let fileName = null;
89
+ let stateName = null;
90
+ let stateId = null;
91
+
92
+ fileName = name.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
93
+ stateName = fileName.charAt( 0 ).toUpperCase() + fileName.slice( 1 );
94
+ stateId = stateName.replace(/[A-Z]/g, (match, offset) => (offset > 0 ? '-' : '') + match.toLowerCase());
95
+
96
+ const stateFilename = path.resolve( '.' ) + path.sep + stateName + 'State.js';
97
+ if ( true === fs.existsSync( stateFilename ) )
98
+ {
99
+ console.error( `File "${stateFilename}" already exists!` );
100
+ return false;
101
+ }
102
+
103
+ const stateTemplatename = path.resolve( '.' ) + path.sep + stateName + 'Template.html';
104
+ if ( true === options.template && true === fs.existsSync( stateTemplatename ) )
105
+ {
106
+ console.error( `File "${stateTemplatename}" already exists!` );
107
+ return false;
108
+ }
109
+
110
+ fs.writeFileSync(
111
+ stateFilename,
112
+ ejs.render( stateClass, { stateName: stateName, stateId : stateId } )
113
+ );
114
+ fs.writeFileSync(
115
+ stateTemplatename,
116
+ ejs.render( stateTemplate, { stateName: stateName } )
117
+ );
118
+
119
+ console.log( chalk.green.bold( `State ${stateName} successfully generated.`) );
120
+ 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} );`) );
122
+ }
30
123
 
124
+ async function generatesWebComponent( name = null )
125
+ {
126
+ let fileName = null;
127
+ let className = null;
128
+ let wcName = null;
31
129
 
130
+ // Deep Copy
131
+ const initConfig = JSON.parse( JSON.stringify( defaultConfig ) );
132
+
133
+ _.unset( initConfig, 'debug.esbuild.plugins' );
134
+ _.unset( initConfig, 'release.esbuild.plugins' );
135
+
136
+ if ( null === name || name.length === 0 )
137
+ {
138
+ console.error( 'V')
139
+ }
140
+
141
+ fileName = name.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
142
+ className = fileName.charAt( 0 ).toUpperCase() + fileName.slice( 1 );
143
+ wcName = name.replace(/[A-Z]/g, (match, offset) => (offset > 0 ? '-' : '') + match.toLowerCase());
144
+ fileName = fileName.charAt(0).toLowerCase() + fileName.slice(1);
145
+
146
+ const srcFolder = path.resolve( '.' ) + path.sep + fileName;
147
+ if ( true === fs.existsSync( srcFolder ) )
148
+ {
149
+ console.error( `Folder "${srcFolder}" already exists!` );
150
+ return false;
151
+ }
152
+
153
+ fs.mkdirSync ( srcFolder ) ;
154
+ fs.writeFileSync(
155
+ srcFolder + path.sep + 'index.js',
156
+ ejs.render( wcIndex, { wcName: wcName, className: className } )
157
+ );
158
+ fs.writeFileSync(
159
+ srcFolder + path.sep + 'template.js',
160
+ ejs.render( wcTemplate, { wcName: wcName } )
161
+ );
162
+
163
+ console.log( chalk.green.bold( `Web component ${wcName} successfully created.` ) );
32
164
  }
33
165
 
34
166
  // Try to load custom config
@@ -61,6 +193,29 @@ program
61
193
  .description( 'Creates frontfire default configuration file in current directory.' )
62
194
  .action( function() { performInit(); } );
63
195
 
196
+ program
197
+ .command( 'create' )
198
+ .description( 'Creates starter InfrontJS application.' )
199
+ .action( function() { createInfrontJsStarter(); } );
200
+
201
+ program
202
+ .command( 'gwc' )
203
+ .argument( '<name>', 'Name of web component.' )
204
+ .description( 'Generates a webcomponent with specific name.' )
205
+ .action( function( name ) { generatesWebComponent( name ); } );
206
+
207
+ program
208
+ .command( 'gs' )
209
+ .argument( '<name>', 'Name of state.' )
210
+ .option( '-nt, --no-template', 'No template is generated. By default, an empty template for the State is autogenerated.' )
211
+ .description( 'Generates a new state.' )
212
+ .action( function( name, options ) { generatesState( name, options ); } );
213
+
214
+ program
215
+ .command( 'gpo' )
216
+ .argument( '<name>', 'Name of path object.' )
217
+ .description( 'Generates a new path object.' )
218
+ .action( function( name ) { generatesPathObject( name ); } );
64
219
 
65
220
  program
66
221
  .command( 'build' )
@@ -0,0 +1,4 @@
1
+ export default 'import { PathObject } from "infrontjs";\n' +
2
+ 'export class <%= poName %> extends PathObject\n' +
3
+ '{\n' +
4
+ '}';
@@ -0,0 +1,12 @@
1
+ export default 'import { BaseState } from "infrontjs";\n' +
2
+ 'import template from \'./<%= stateName %>Template.html\';\n' +
3
+ 'export class <%= stateName %> extends BaseState\n' +
4
+ '{\n' +
5
+ ' static ROUTE = "/<%= stateId %>";\n' +
6
+ ' static ID = "<%= stateId %>";\n' +
7
+ '\n' +
8
+ ' async enter()\n' +
9
+ ' {\n' +
10
+ ' console.log( "Hello from state <%= stateName %>" );\n' +
11
+ ' }\n' +
12
+ '}';
@@ -0,0 +1 @@
1
+ export default '<h1>Hello from State <%= stateName %>!</h1>';
@@ -0,0 +1,32 @@
1
+ export default '' +
2
+ '// import * as IF from "infrontjs";\n' +
3
+ 'import template from "./template.html";\n' +
4
+ 'class <%= className %> extends HTMLElement\n' +
5
+ '{\n' +
6
+ ' constructor()\n' +
7
+ ' {\n' +
8
+ ' super();\n' +
9
+ ' this._isConnected = false;\n' +
10
+ ' }\n' +
11
+ '\n' +
12
+ ' connectedCallback()\n' +
13
+ ' {\n' +
14
+ ' if ( false === this._isConnected )\n' +
15
+ ' {\n' +
16
+ ' // Resolve default InfrontJS instance\n' +
17
+ ' // this.app = IF.App.get();\n' +
18
+ ' this.render();\n' +
19
+ '\n' +
20
+ ' this._isConnected = true;\n' +
21
+ ' }\n' +
22
+ ' }\n' +
23
+ '\n' +
24
+ ' render()\n' +
25
+ ' {\n' +
26
+ ' // Eg. Render template\n' +
27
+ ' // this.innerHTML = this.app.view.getHtml( template, { user : this.app.user } );\n' +
28
+ ' this.innerHTML = template;\n' +
29
+ ' }\n' +
30
+ '}\n' +
31
+ '\n' +
32
+ 'customElements.define(<%= wcName %>, <%= className %> );'
@@ -0,0 +1,2 @@
1
+ export default '' +
2
+ '<strong>Content of web component named:<br /><%= wcName %></strong>';