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.
@@ -1,241 +0,0 @@
1
- /*----------------------------------
2
- - DEPENDANCES
3
- ----------------------------------*/
4
-
5
- // Npm
6
- import * as types from '@babel/types'
7
- import type { PluginObj, NodePath } from '@babel/core';
8
- import generate from '@babel/generator';
9
-
10
- // Core
11
- import type { TAppSide } from '@cli/index';
12
-
13
- /*----------------------------------
14
- - WEBPACK RULE
15
- ----------------------------------*/
16
-
17
- type TOptions = {
18
- side: TAppSide
19
- }
20
-
21
- module.exports = (options: TOptions) => ({
22
- test: "**/server/models/**/*.ts",
23
- plugins: [
24
- [Plugin, options]
25
- ]
26
- })
27
-
28
- module.exports.Plugin = Plugin;
29
-
30
- const debug = true;
31
-
32
- const metasKey = 'metadata';
33
-
34
- /*----------------------------------
35
- - PLUGIN
36
- ----------------------------------*/
37
- function Plugin(babel, { }: TOptions) {
38
-
39
- const t = babel.types as typeof types;
40
-
41
- const plugin: PluginObj<{ filename: string }> = {
42
- pre(state) {
43
-
44
- this.filename = state.opts.filename as string;
45
-
46
- },
47
- visitor: {
48
- Program(path) {
49
-
50
- const filename = this.filename;
51
-
52
- // Lorsque les visiteurs sont déclarés à la racine du plugin,
53
- // Impossible d'obtenir les ast des décorateurs car ils ont déjà été compilés ...
54
- // Pourquoi cela fonctionne en passant par Program.traverse ?
55
- path.traverse({
56
- Decorator(path) {
57
-
58
- injectMetadatas(path, filename, t);
59
-
60
- },
61
- });
62
-
63
-
64
- }
65
-
66
- }
67
- };
68
-
69
- return plugin;
70
- }
71
-
72
- /*----------------------------------
73
- - EXTRACTION
74
- ----------------------------------*/
75
- function injectMetadatas( path: NodePath<types.Decorator>, filename: string, t: typeof types ) {
76
-
77
- // @Table( <database>, <table>, <options?> )
78
- const expression = path.node.expression;
79
- if (!(
80
- expression.type === 'CallExpression'
81
- &&
82
- expression.callee.type === 'Identifier'
83
- &&
84
- expression.callee.name === 'Table'
85
- &&
86
- expression.arguments.length >= 2
87
- ))
88
- return;
89
-
90
- // Class
91
- const classe = path.parent;
92
- if (classe.type !== 'ClassDeclaration')
93
- return;
94
-
95
- debug && console.log(`Processing class ${classe.id.name}`);
96
-
97
- // Only process if metadata are not already defined
98
- let [database, table, options] = expression.arguments;
99
- if (options !== undefined) {
100
-
101
- if (options.type !== 'ObjectExpression')
102
- throw new Error(`Error in ${filename}: The 3rd argument of @Table must be an object expression.`);
103
-
104
- const hasMetas = options.properties.some(p =>
105
- p.type === 'ObjectProperty'
106
- &&
107
- p.key.type === 'Identifier'
108
- &&
109
- p.key.name === metasKey
110
- );
111
-
112
- if (hasMetas) {
113
- debug && console.log(`Metadata already provides for class ${classe.id.name} (${filename})`);
114
- return;
115
- }
116
- }
117
-
118
- // Extract metadata
119
- const attributes = extractMetadata(classe, t);
120
- //debug && console.log( generate(attributes).code );
121
- const metasProperty = t.objectProperty(
122
- t.identifier(metasKey),
123
- t.objectExpression(attributes)
124
- )
125
-
126
- // Insert metas in options
127
- if (options === undefined)
128
- options = t.objectExpression([metasProperty]);
129
- else
130
- options = t.objectExpression([...options.properties, metasProperty]);
131
-
132
- // Update decorator
133
- path.replaceWith(t.decorator(
134
- t.callExpression(t.identifier('Table'), [
135
- database,
136
- table,
137
- options
138
- ])
139
- ));
140
- }
141
-
142
- function extractMetadata(classe: types.ClassDeclaration, t: typeof types) {
143
-
144
- const attributes: types.ObjectProperty[] = [];
145
-
146
- // Lecture des propriétés et méthodes de la classe
147
- for (const prop of classe.body.body) {
148
-
149
- if (!(
150
- // non-statique
151
- !prop.static
152
- &&
153
- (
154
- // Propriété simple
155
- prop.type === 'ClassProperty'
156
- ||
157
- // Getter
158
- (prop.type === 'ClassMethod' && prop.kind === 'get')
159
- )
160
- &&
161
- // Publique
162
- prop.accessibility === 'public'
163
- &&
164
- prop.key.type === 'Identifier'
165
- ))
166
- continue;
167
-
168
- const nomProp = prop.key.name;
169
-
170
- // Détermine si la propriété est exposée à l'api
171
- // = possède le décorateur @API()
172
- const exposeToAPI = prop.decorators && prop.decorators.some((decorateur) =>
173
- decorateur.expression.type === 'CallExpression'
174
- &&
175
- decorateur.expression.callee.type === 'Identifier'
176
- &&
177
- decorateur.expression.callee.name === 'API'
178
- );
179
-
180
- if (!exposeToAPI) continue;
181
-
182
- let type: any;
183
- if (prop.type === 'ClassProperty')
184
- type = prop.typeAnnotation;
185
- else
186
- type = prop.returnType;
187
-
188
- // Verif si type spécifié
189
- if (!type)
190
- throw new Error(`Unable to extract type of ${classe.id.name}.${prop.key.name}.`);
191
-
192
- // Sérialisation du type
193
- const typeString = generate(type.typeAnnotation).code;
194
-
195
- debug && console.log( classe.id.name + '.' + nomProp + ': ' + typeString );
196
-
197
- // Sérialisation valeur defaut
198
- const defaut = prop.type === 'ClassProperty' && prop.value && ('value' in prop.value)
199
- ? prop.value
200
- : t.identifier('undefined')
201
-
202
- attributes.push(
203
- t.objectProperty( t.identifier(nomProp), t.objectExpression([
204
-
205
- t.objectProperty(
206
- t.identifier('nom'),
207
- t.stringLiteral(nomProp)
208
- ),
209
-
210
- t.objectProperty(
211
- t.identifier('nomComplet'),
212
- t.stringLiteral(prop.optional ? nomProp + '?' : nomProp)
213
- ),
214
-
215
- t.objectProperty(
216
- t.identifier('type'),
217
- t.stringLiteral(typeString)
218
- ),
219
-
220
- t.objectProperty(
221
- t.identifier('optionnel'),
222
- t.booleanLiteral(prop.optional === true)
223
- ),
224
-
225
- t.objectProperty(
226
- t.identifier('api'),
227
- t.booleanLiteral(exposeToAPI)
228
- ),
229
-
230
- t.objectProperty(
231
- t.identifier('defaut'),
232
- defaut
233
- ),
234
-
235
- ]))
236
- );
237
- }
238
-
239
- return attributes;
240
-
241
- }
@@ -1,152 +0,0 @@
1
- /*----------------------------------
2
- - DEPENDANCES
3
- ----------------------------------*/
4
-
5
- // Npm
6
- import * as types from '@babel/types'
7
- import type { PluginObj, NodePath } from '@babel/core';
8
- import generate from '@babel/generator';
9
-
10
- // Core
11
- import cli from '@cli';
12
- import App, { TAppSide } from '../../../../app';
13
-
14
- /*----------------------------------
15
- - WEBPACK RULE
16
- ----------------------------------*/
17
-
18
- type TOptions = {
19
- side: TAppSide
20
- }
21
-
22
- module.exports = (options: TOptions) => ({
23
- test: "**/client/pages/**/*.tsx",
24
- plugins: [
25
- [Plugin, options]
26
- ]
27
- })
28
-
29
- const debug = false;
30
-
31
- /*----------------------------------
32
- - PLUGIN
33
- ----------------------------------*/
34
- function Plugin(babel, { side }: TOptions) {
35
-
36
- const app = new App();
37
-
38
- const t = babel.types as typeof types;
39
- let program: NodePath<types.Program>;
40
-
41
- const plugin: PluginObj<{
42
- filename: string
43
- }> = {
44
- pre(state) {
45
-
46
- this.filename = state.opts.filename as string;
47
-
48
- },
49
- visitor: {
50
-
51
- Program(path) {
52
- program = path;
53
- },
54
-
55
- CallExpression(path) {
56
-
57
- // route.xxx( <PATH>, ... )
58
- if (!(
59
- path.node.callee.type === 'MemberExpression'
60
- &&
61
- path.node.callee.object.type === "Identifier"
62
- &&
63
- path.node.callee.object.name === "route"
64
- &&
65
- path.node.arguments.length >= 2
66
- ))
67
- return;
68
-
69
- const [routePath, ...routeArgs] = path.node.arguments;
70
- debug && console.log(`[routes]`, this.filename, ...routeArgs.map(n => n.type));
71
-
72
- // Inject chunk id in options (2nd arg)
73
- const status = addChunkId(routeArgs, this.filename);
74
- if (status === 'ALREADY_PROCESSED')
75
- return;
76
-
77
- // Transform 2nd arg of renderer to a useContext spread
78
- addRendererContext(routeArgs, this.filename);
79
-
80
- const replacement = t.callExpression( path.node.callee, [ routePath, ...routeArgs ]);
81
- debug && console.log( generate(replacement).code );
82
-
83
- path.replaceWith( replacement );
84
-
85
- // Force export default
86
- // NOTE: now done by app-import.ts
87
- /*if (path.parent.type === 'ExportDefaultDeclaration')
88
- path.replaceWith( replacement );
89
- else
90
- path.parentPath.replaceWith(
91
- t.exportDefaultDeclaration( replacement )
92
- )*/
93
-
94
- }
95
-
96
- }
97
- };
98
-
99
- function addRendererContext(
100
- routeArgs: types.CallExpression["arguments"],
101
- filename: string
102
- ) {
103
-
104
- // ( <data>, { response, api }) => ....
105
- const renderer = routeArgs[ routeArgs.length - 1 ];
106
- if (!(
107
- renderer.type === 'ArrowFunctionExpression'
108
- &&
109
- renderer.params.length > 1
110
- ))
111
- return;
112
-
113
- // Remove 2nd arg (renderer = react component, so only 1 arg for props)
114
- const declaration = renderer.params.pop() as types.ArrowFunctionExpression["params"][number];
115
- // const <param2> = useContext();
116
- const ctxDeclaration = t.variableDeclaration('const', [t.variableDeclarator(
117
- declaration,
118
- t.callExpression(t.identifier('useContext'), [])
119
- )])
120
-
121
- // Add Declaration
122
- switch (renderer.body.type) {
123
- case 'BlockStatement':
124
- renderer.body.body.unshift(ctxDeclaration);
125
- break;
126
- // TODO: Si type === JSXElement, remplacer par BlockStatement (ctxDeclaration + return JSX)
127
- /*case 'BlockStatement':
128
- renderer.re
129
- break;*/
130
- default:
131
- throw new Error(`Unknown body type for the renderer: ${renderer.body.type}`);
132
- }
133
-
134
- // Add usecontext import if it doesn't exists
135
- if (program.scope.bindings["useContext"] === undefined) {
136
-
137
- debug && console.log(`[routes]`, filename, `Adding useContext import from @context`);
138
-
139
- program.unshiftContainer(
140
- 'body',
141
- t.importDeclaration(
142
- [t.importDefaultSpecifier(t.identifier('useContext'))],
143
- t.stringLiteral('@/client/context')
144
- )
145
- );
146
- }
147
-
148
-
149
- }
150
-
151
- return plugin;
152
- }
@@ -1,46 +0,0 @@
1
- /*----------------------------------
2
- - DEPENDANCES
3
- ----------------------------------*/
4
-
5
- // Npm
6
- import path from 'path';
7
- import fs from 'fs-extra';
8
-
9
- // App
10
- import type App from '../../../app';
11
-
12
- /*----------------------------------
13
- - TYPES
14
- ----------------------------------*/
15
-
16
- /*----------------------------------
17
- - UTTILS
18
- ----------------------------------*/
19
- export const fixNpmLinkIssues = ( app: App ) => {
20
-
21
- const corePath = path.join(app.paths.root, '/node_modules/5htp-core');
22
- if (!fs.lstatSync( corePath ).isSymbolicLink())
23
- return console.info("Not fixing npm issue because 5htp-core wasn't installed with npm link.");
24
-
25
- console.info(`Fix NPM link issues ...`);
26
-
27
- const appModules = path.join(app.paths.root, 'node_modules');
28
- const coreModules = path.join(corePath, 'node_modules');
29
-
30
- // When the 5htp package is installed from npm link,
31
- // Modules are installed locally and not glbally as with with the 5htp package from NPM.
32
- // So we need to symbilnk the http-core node_modules in one of the parents of server.js.
33
- // It avoids errors like: "Error: Cannot find module 'intl'"
34
- fs.symlinkSync( coreModules, path.join(app.paths.bin, 'node_modules') );
35
-
36
- // Same problem: when 5htp-core is installed via npm link,
37
- // Typescript doesn't detect React and shows mission JSX errors
38
- const preactCoreModule = path.join(coreModules, 'preact');
39
- const preactAppModule = path.join(appModules, 'preact');
40
- const reactAppModule = path.join(appModules, 'react');
41
-
42
- if (!fs.existsSync( preactAppModule ))
43
- fs.symlinkSync( preactCoreModule, preactAppModule );
44
- if (!fs.existsSync( reactAppModule ))
45
- fs.symlinkSync( path.join(preactCoreModule, 'compat'), reactAppModule );
46
- }