5htp 0.2.3 → 0.3.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,7 +1,7 @@
1
1
  {
2
2
  "name": "5htp",
3
3
  "description": "Convenient TypeScript framework designed for Performance and Productivity.",
4
- "version": "0.2.3",
4
+ "version": "0.3.0",
5
5
  "author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
6
6
  "repository": "git://github.com/gaetanlegac/5htp.git",
7
7
  "license": "MIT",
@@ -41,7 +41,6 @@
41
41
  "@types/webpack-env": "^1.16.2",
42
42
  "@types/ws": "^7.4.7",
43
43
  "babel-loader": "^8.2.2",
44
- "babel-plugin-glob-import": "^0.0.6-3",
45
44
  "babel-plugin-transform-imports": "^2.0.0",
46
45
  "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
47
46
  "brotli-webpack-plugin": "^1.1.0",
@@ -73,7 +72,6 @@
73
72
  "terser-webpack-plugin": "^5.2.4",
74
73
  "ts-alias": "^0.0.7",
75
74
  "ts-node": "^10.9.1",
76
- "tslog": "^3.3.4",
77
75
  "webfont": "^11.2.26",
78
76
  "webpack": "^5.56.0",
79
77
  "webpack-assets-manifest": "^5.0.6",
@@ -89,6 +87,7 @@
89
87
  "@types/node": "^16.9.1",
90
88
  "@types/nodemailer": "^6.4.4",
91
89
  "@types/prompts": "^2.0.14",
92
- "@types/webpack-env": "^1.16.2"
90
+ "@types/webpack-env": "^1.16.2",
91
+ "babel-plugin-glob-import": "^0.0.7"
93
92
  }
94
93
  }
package/readme.md CHANGED
@@ -41,16 +41,20 @@ Full Stack Typescript framework designed for **developers and users experience**
41
41
  ## To be done:
42
42
 
43
43
  - [ ] Update templates & documentation
44
- - [ ] Improved debugability & debug tools
45
- - Cleaner logs (tweak ts-logs settings)
46
- - Cleaner stacktraces
47
- - export all default exports as classical functions instead of arrow funcs
44
+ - [ ] HMR
45
+ - [ ] Debugging / Monitoring UI
46
+ - All services and status: installed / paused / running
47
+ - Router:
48
+ - list of registered files
49
+ - list of pages / api / error routes / layouts
50
+ - latest requests + performance
51
+ - Logs: recent logs terminal
52
+ - Database: recent queries + performance
53
+ - Page rendering: performance
54
+ - Socket: who is connected
55
+ - [ ] Add testing tools
56
+ - [ ] Possibility to generate static pages
57
+ - [ ] Improve ORM: make definition based on code instead of database structure
48
58
  - [ ] Automatically generates types that associate api routes urls to their return types
49
- - [ ] Fix forms system
50
59
  - [ ] Allow to create CLI apps
51
- - [ ] Debugging / Monitoring UI
52
- - Admin dashboard
53
- - [ ] Fix Typescript errors
54
- - [ ] Completely replace expressjs
55
- - [ ] Improve stability
56
- - [ ] Improve modularity
60
+ - [ ] Fix Typescript errors
@@ -0,0 +1,117 @@
1
+ export type BugApp = {
2
+ action: string,
3
+ build: string,
4
+ client: string,
5
+ date: Date,
6
+ device?: string,
7
+ hash: string,
8
+ ip: string,
9
+ message?: string,
10
+ side: string,
11
+ solved?: Date,
12
+ stacktrace: string,
13
+ status?: string,
14
+ user?: string
15
+ }
16
+ export type BugGUI = {
17
+ before?: string,
18
+ date: Date,
19
+ device: string,
20
+ guiVersion: string,
21
+ hash: string,
22
+ ip: string,
23
+ observation?: string,
24
+ serverVersion: string,
25
+ solved?: Date,
26
+ context?: object,
27
+ stacktrace?: string,
28
+ url: string,
29
+ user?: string
30
+ }
31
+ export type BugServer = {
32
+ channelId?: string,
33
+ channelType: string,
34
+ date: Date,
35
+ hash: string,
36
+ ip?: string,
37
+ logs: string,
38
+ solved?: Date,
39
+ stacktrace: string,
40
+ user?: string
41
+ }
42
+ export type Countries = {
43
+ id: string,
44
+ name: string
45
+ }
46
+ export type Devices = {
47
+ activity: Date,
48
+ bandwidth: number,
49
+ build: number,
50
+ connectedSince: Date,
51
+ id: string,
52
+ invalid: number,
53
+ ip: string,
54
+ meet: Date,
55
+ name?: string,
56
+ platform: string,
57
+ rewards: number,
58
+ user: string,
59
+ valid: number
60
+ }
61
+ export type Locales = {
62
+ id: string,
63
+ name: string
64
+ }
65
+ export type User = {
66
+ activity: Date,
67
+ balance: number,
68
+ balanceGoal: number,
69
+ balanceGoalReached: number,
70
+ banned?: Date,
71
+ boosterEnd?: Date,
72
+ country?: string,
73
+ donationPc: number,
74
+ email: string,
75
+ emailHash: string,
76
+ exp: number,
77
+ level: number,
78
+ levelsReached: number,
79
+ meet: Date,
80
+ name: string,
81
+ phone?: string,
82
+ referrer?: string,
83
+ refPeriodEnd?: Date,
84
+ roles: string[],
85
+ smscode?: string,
86
+ utm?: string
87
+ }
88
+ export type UserLogin = {
89
+ date: Date,
90
+ device: string,
91
+ id: number,
92
+ ip: string,
93
+ user: string
94
+ }
95
+ export type UserStats = {
96
+ activity: Date,
97
+ balance: number,
98
+ bandwidth: number,
99
+ date: Date,
100
+ failures: number,
101
+ refClics: number,
102
+ refCommission: number,
103
+ refSignups: number,
104
+ success: number,
105
+ user: string
106
+ }
107
+ export type Withdraw = {
108
+ address: string,
109
+ amount: number,
110
+ btcPrice: number,
111
+ date: Date,
112
+ fees: number,
113
+ id: number,
114
+ sent: number,
115
+ transactionId?: string,
116
+ user: string
117
+ }
package/src/app/config.ts CHANGED
@@ -14,6 +14,9 @@
14
14
  import fs from 'fs-extra';
15
15
  import yaml from 'yaml';
16
16
 
17
+ // Types
18
+ import type { TEnvConfig } from '../../../core/src/server/app/container/config';
19
+
17
20
  /*----------------------------------
18
21
  - LOADE
19
22
  ----------------------------------*/
@@ -32,10 +35,9 @@ export default class ConfigParser {
32
35
  return yaml.parse(rawConfig);
33
36
  }
34
37
 
35
- public env() {
38
+ public env(): TEnvConfig {
36
39
  // We assume that when we run 5htp dev, we're in local
37
40
  // Otherwise, we're in production environment (docker)
38
- console.log("[cli] Using environment:", process.env.NODE_ENV);
39
41
  return process.env.NODE_ENV === 'development' ? {
40
42
  name: 'local',
41
43
  profile: 'dev',
package/src/app/index.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  ----------------------------------*/
4
4
 
5
5
  // npm
6
+ import path from 'path';
6
7
  import TsAlias from 'ts-alias';
7
8
 
8
9
  // Cre
@@ -10,7 +11,7 @@ import cli from '..';
10
11
 
11
12
  // Specific
12
13
  import ConfigParser from './config';
13
- import type { } from '../../../core/src/server/app/config';
14
+ import type { TEnvConfig } from '../../../core/src/server/app/container/config';
14
15
 
15
16
  /*----------------------------------
16
17
  - TYPES
@@ -21,21 +22,32 @@ export type TAppSide = 'server' | 'client'
21
22
  /*----------------------------------
22
23
  - SERVICE
23
24
  ----------------------------------*/
24
- export default class App {
25
+ export class App {
25
26
 
26
27
  // config
27
28
  // WARNING: High level config files (env and services) shouldn't be loaded from the CLI
28
29
  // The CLI will be run on CircleCI, and no env file should be sent to this service
29
30
  public identity: Config.Identity;
30
31
 
32
+ public env: TEnvConfig;
33
+
31
34
  public paths = {
35
+
32
36
  root: cli.paths.appRoot,
33
- src: cli.paths.appRoot + '/src',
34
- bin: cli.paths.appRoot + '/bin',
35
- data: cli.paths.appRoot + '/var/data',
36
- public: cli.paths.appRoot + '/public',
37
- pages: cli.paths.appRoot + '/src/client/pages',
38
- cache: cli.paths.appRoot + '/src/.cache',
37
+ src: path.join( cli.paths.appRoot, 'src'),
38
+ bin: path.join( cli.paths.appRoot, 'bin'),
39
+ data: path.join( cli.paths.appRoot, 'var', 'data'),
40
+ public: path.join( cli.paths.appRoot, 'public'),
41
+ pages: path.join( cli.paths.appRoot, 'src', 'client', 'pages'),
42
+ cache: path.join( cli.paths.appRoot, 'src', '.cache'),
43
+
44
+ client: {
45
+ generated: path.join( cli.paths.appRoot, 'src', 'client', '.generated')
46
+ },
47
+ server: {
48
+ generated: path.join( cli.paths.appRoot, 'src', 'server', '.generated'),
49
+ configs: path.join( cli.paths.appRoot, 'src', 'server', 'app')
50
+ },
39
51
 
40
52
  withAlias: (filename: string, side: TAppSide) =>
41
53
  this.aliases[side].apply(filename),
@@ -46,9 +58,10 @@ export default class App {
46
58
 
47
59
  public constructor() {
48
60
 
49
- console.log(`[cli] Loading app config ...`);
61
+ cli.debug && console.log(`[cli] Loading app config ...`);
50
62
  const configParser = new ConfigParser( cli.paths.appRoot );
51
63
  this.identity = configParser.identity();
64
+ this.env = configParser.env();
52
65
  }
53
66
 
54
67
  /*----------------------------------
@@ -73,4 +86,8 @@ export default class App {
73
86
  debug: false
74
87
  }),
75
88
  }
76
- }
89
+ }
90
+
91
+ export const app = new App
92
+
93
+ export default app
@@ -6,10 +6,7 @@
6
6
  import prompts from 'prompts';
7
7
 
8
8
  // Configs
9
- import createCompilers from '../compiler';
10
-
11
- // Core
12
- import App from '../app';
9
+ import Compiler from '../compiler';
13
10
 
14
11
  /*----------------------------------
15
12
  - TYPES
@@ -20,9 +17,9 @@ import App from '../app';
20
17
  ----------------------------------*/
21
18
  export const run = (): Promise<void> => new Promise(async (resolve) => {
22
19
 
23
- const app = new App();
20
+ const compiler = new Compiler('prod');
24
21
 
25
- const multiCompiler = await createCompilers(app, 'prod');
22
+ const multiCompiler = await compiler.create();
26
23
 
27
24
  multiCompiler.run((error, stats) => {
28
25
 
@@ -11,19 +11,17 @@ import cli from '../';
11
11
  import Keyboard from '../utils/keyboard';
12
12
 
13
13
  // Configs
14
- import createCompilers, { compiling } from '../compiler';
14
+ import Compiler from '../compiler';
15
15
 
16
16
  // Core
17
- import App from '../app';
17
+ import { app, App } from '../app';
18
18
 
19
19
  /*----------------------------------
20
20
  - COMMANDE
21
21
  ----------------------------------*/
22
22
  export const run = () => new Promise<void>(async () => {
23
-
24
- const app = new App();
25
23
 
26
- const multiCompiler = await createCompilers(app, 'dev', {
24
+ const compiler = new Compiler('dev', {
27
25
  before: () => {
28
26
 
29
27
  stopApp();
@@ -35,6 +33,8 @@ export const run = () => new Promise<void>(async () => {
35
33
  }
36
34
  });
37
35
 
36
+ const multiCompiler = await compiler.create();
37
+
38
38
  multiCompiler.watch({
39
39
 
40
40
  // https://webpack.js.org/configuration/watch/#watchoptions
@@ -60,8 +60,8 @@ export const run = () => new Promise<void>(async () => {
60
60
 
61
61
  Keyboard.input('ctrl+r', async () => {
62
62
 
63
- console.log(`Waiting for compilers to be ready ...`, Object.keys(compiling));
64
- await Promise.all(Object.values(compiling));
63
+ console.log(`Waiting for compilers to be ready ...`, Object.keys(compiler.compiling));
64
+ await Promise.all(Object.values(compiler.compiling));
65
65
 
66
66
  console.log(`Reloading app ...`);
67
67
  startApp(app);
@@ -178,7 +178,7 @@ export default function createCompiler( app: App, mode: TCompileMode ): webpack.
178
178
  ...c.chunks.reduce(
179
179
  (files, cc) => [
180
180
  ...files,
181
- ...cc.files.filter(fileFilter).map(addPath),
181
+ ...[...cc.files].filter(fileFilter).map(addPath),
182
182
  ],
183
183
  [],
184
184
  ),
@@ -124,12 +124,11 @@ module.exports = (app: App, side: TAppSide, dev: boolean): webpack.RuleSetRule[]
124
124
 
125
125
  //require("./plugins/pages")({ side }),
126
126
 
127
- require('./routes/routes')({ side, app }),
127
+ require('./routes/routes')({ side, app, debug: false }),
128
128
 
129
129
  ...(side === 'client' ? [] : [
130
130
 
131
- // Dependancies injection
132
- //require('./plugins/services')({ side }),
131
+ require('./plugins/services')({ side, app, debug: false }),
133
132
 
134
133
  ]),
135
134
 
@@ -4,157 +4,189 @@
4
4
 
5
5
  // Npm
6
6
  import * as types from '@babel/types'
7
- import type { PluginObj, NodePath } from '@babel/core';
8
- import generate from '@babel/generator';
7
+ import type { PluginObj } from '@babel/core';
9
8
 
10
9
  // Core
11
10
  import cli from '@cli';
12
- import App, { TAppSide } from '../../../../app';
11
+ import { App, TAppSide } from '../../../../app';
12
+
13
+ const containerServices = [
14
+ 'Environment',
15
+ 'Application',
16
+ 'Path',
17
+ 'Services',
18
+ 'Event'
19
+ ]
13
20
 
14
21
  /*----------------------------------
15
22
  - WEBPACK RULE
16
23
  ----------------------------------*/
17
24
 
18
25
  type TOptions = {
19
- side: TAppSide
26
+ side: TAppSide,
27
+ app: App,
28
+ debug?: boolean
20
29
  }
21
30
 
22
- const filenamePrefix = cli.paths.appRoot + '/src/server/services';
23
- const processFile = (filename: string) => (
24
- filename.startsWith( cli.paths.appRoot + '/src/server/services' )
25
- |
26
- filename.startsWith( cli.paths.appRoot + '/src/server/routes' )
27
- )
31
+ type TImportSource = 'container' | 'application';
28
32
 
29
33
  module.exports = (options: TOptions) => (
30
34
  [Plugin, options]
31
35
  )
32
36
 
33
- const debug = true;
34
37
 
35
38
  /*----------------------------------
36
39
  - PLUGIN
37
40
  ----------------------------------*/
38
- function Plugin(babel, { side }: TOptions) {
41
+ function Plugin(babel, { app, side, debug }: TOptions) {
39
42
 
40
43
  const t = babel.types as typeof types;
41
- let program: NodePath<types.Program>;
44
+
45
+ /*
46
+ - Wrap route.get(...) with (app: Application) => { }
47
+ - Inject chunk ID into client route options
48
+ */
42
49
 
43
50
  const plugin: PluginObj<{
44
51
 
45
- filename: string,
52
+ debug: boolean,
46
53
 
47
- appImport: string | null,
54
+ filename: string,
55
+ processFile: boolean,
48
56
 
49
57
  // Identifier => Name
50
- importedServices: {[identifier: string]: string}
58
+ importedServicesCount: number,
59
+ importedServices: {
60
+ [local: string]: {
61
+ imported: string,
62
+ bindings: any, // TODO: Scope.Binding[] type
63
+ source: TImportSource
64
+ }
65
+ },
66
+ bySource: {[importSource in TImportSource] : number}
51
67
  }> = {
52
68
  pre(state) {
53
69
 
54
70
  this.filename = state.opts.filename as string;
71
+ this.processFile = this.filename.startsWith( cli.paths.appRoot + '/src/server' );
55
72
 
56
- this.appImport = null
57
73
  this.importedServices = {}
74
+ this.bySource = {
75
+ container: 0,
76
+ application: 0
77
+ }
78
+ this.importedServicesCount = 0
79
+ this.debug = debug || false;
58
80
 
59
81
  },
60
82
  visitor: {
61
83
 
62
- Program(path) {
63
- program = path;
64
- },
65
-
66
- // Transform imports
84
+ // Find @app imports
85
+ // Test: import { router } from '@app';
86
+ // Replace by: nothing
67
87
  ImportDeclaration(path) {
68
88
 
69
- if (!this.filename.startsWith( cli.paths.appRoot + '/src/server' ))
89
+ if (!this.processFile)
70
90
  return;
71
91
 
72
- if (path.node.source.value !== '@server/app')
92
+ if (path.node.source.value !== '@app')
73
93
  return;
74
94
 
75
- const importedServices: { local: string, imported: string }[] = []
76
- let appName: string = 'app';
77
-
78
95
  for (const specifier of path.node.specifiers) {
79
- /*
80
- import app from '@server/app';
81
- */
82
- if (specifier.type === 'ImportDefaultSpecifier') {
96
+
97
+ if (specifier.type !== 'ImportSpecifier')
98
+ continue;
83
99
 
84
- appName = specifier.local.name;
100
+ if (specifier.imported.type !== 'Identifier')
101
+ continue;
85
102
 
86
- /*
87
- import { sql } from '@server/app';
88
- =>
89
- import app from '@server/app';
90
- app.use('sql');
91
- */
92
- } else if (specifier.type === 'ImportSpecifier') {
103
+ this.importedServicesCount++;
93
104
 
94
- if (specifier.imported.type !== 'Identifier')
95
- continue;
105
+ let importSource: TImportSource;
106
+ if (containerServices.includes(specifier.imported.name))
107
+ importSource = 'container';
108
+ else
109
+ importSource = 'application';
96
110
 
97
- importedServices.push({
98
- local: specifier.local.name,
99
- imported: specifier.imported.name
100
- });
111
+ this.importedServices[ specifier.local.name ] = {
112
+ imported: specifier.imported.name,
113
+ bindings: path.scope.bindings[ specifier.local.name ].referencePaths,
114
+ source: importSource
115
+ }
101
116
 
102
- /*
103
- import * as templates from '@server/app';
104
- =>
117
+ this.bySource[ importSource ]++;
118
+ }
105
119
 
106
- */
107
- } else if (specifier.type === 'ImportNamespaceSpecifier') {
120
+ // Replace by simple import
121
+ this.debug && console.log("[babel][services] Replace importation");
122
+ const replaceWith: any[] = []
108
123
 
109
- //importDefault = specifier.local.name;
110
- //importAll = true;
124
+ if (this.bySource.container > 0)
125
+ replaceWith.push(
126
+ t.importDeclaration(
127
+ [t.importDefaultSpecifier( t.identifier('container') )],
128
+ t.stringLiteral( cli.paths.core.src + '/server/app/container')
129
+ )
130
+ );
111
131
 
112
- }
113
- }
132
+ if (this.bySource.application > 0)
133
+ replaceWith.push(
134
+ t.importDeclaration(
135
+ [t.importDefaultSpecifier( t.identifier('application') )],
136
+ t.stringLiteral( cli.paths.core.src + '/server/app/instance')
137
+ )
138
+ );
139
+
140
+ path.replaceWithMultiple(replaceWith);
141
+ },
114
142
 
115
- // No service imported
116
- // This verification avoids ininite loop
117
- if (importedServices.length === 0)
143
+ Identifier(path) {
144
+
145
+ if (!this.processFile || this.importedServicesCount === 0)
118
146
  return;
119
147
 
120
- const replacements: types.Statement[] = [
121
- t.importDeclaration(
122
- [
123
- t.importDefaultSpecifier( t.identifier( appName )),
124
- ],
125
- t.stringLiteral('@server/app')
126
- )
127
- ]
128
-
129
- for (const { imported, local } of importedServices) {
130
-
131
- replacements.push(
132
- t.expressionStatement(
133
- t.callExpression(
134
- t.memberExpression(
135
- t.identifier( appName ),
136
- t.identifier('use')
137
- ),
138
- [
139
- t.stringLiteral( imported )
140
- ]
141
- )
142
- )
143
- );
148
+ // Get service the identifier makes rfeerence to
149
+ const name = path.node.name;
150
+ const service = this.importedServices[ name ];
151
+ if (service === undefined)
152
+ return;
153
+
154
+ // sometimes not iterable
155
+ if (!service.bindings)
156
+ return;
157
+
158
+ // Replace by app.services.name
159
+ let serviceBinding: any;
160
+ for (const binding of service.bindings) {
161
+ if (binding.replaced !== true && path.getPathLocation() === binding.getPathLocation()) {
162
+
163
+ serviceBinding = binding;
144
164
 
145
- this.importedServices[ local ] = imported;
165
+ break;
166
+ }
146
167
  }
147
168
 
148
- debug && console.log(`############ [compilation][babel][services] Remplacement: `,
149
- generate(t.program(replacements)).code
150
- );
169
+ // This identifier is a binding to a service
170
+ if (serviceBinding === undefined)
171
+ return;
151
172
 
152
- path.replaceWithMultiple(replacements);
153
- },
173
+ // Replace to reference to app.services.serviceName
174
+ path.replaceWith(
175
+ t.memberExpression(
176
+ service.source === 'container'
177
+ // container.Environment
178
+ ? t.identifier( service.source )
179
+ // application.services.Disks
180
+ : t.memberExpression(
181
+ t.identifier( service.source ),
182
+ t.identifier('services'),
183
+ ),
184
+ path.node
185
+ )
186
+ );
154
187
 
155
- // transform accesses
156
- Identifier() {
157
-
188
+ // Avoid circular loop
189
+ serviceBinding.replaced = true;
158
190
  }
159
191
  }
160
192
  }