5htp 0.0.2
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 +94 -0
- package/src/commands/build.ts +32 -0
- package/src/commands/deploy/app.ts +29 -0
- package/src/commands/deploy/web.ts +62 -0
- package/src/commands/dev.ts +100 -0
- package/src/compiler/client/identite.ts +70 -0
- package/src/compiler/client/index.ts +335 -0
- package/src/compiler/common/babel/index.ts +261 -0
- package/src/compiler/common/babel/plugins/form.ts +191 -0
- package/src/compiler/common/babel/plugins/icones-svg.ts +350 -0
- package/src/compiler/common/babel/plugins/importations.ts +337 -0
- package/src/compiler/common/babel/plugins/injection-dependances/index.ts +223 -0
- package/src/compiler/common/babel/plugins/injection-dependances/remplacerFonction.ts +226 -0
- package/src/compiler/common/babel/plugins/models.ts +241 -0
- package/src/compiler/common/babel/plugins/pages.ts +185 -0
- package/src/compiler/common/babel/plugins/queries/index.ts +166 -0
- package/src/compiler/common/files/autres.ts +37 -0
- package/src/compiler/common/files/images.ts +19 -0
- package/src/compiler/common/files/style.ts +64 -0
- package/src/compiler/common/index.ts +148 -0
- package/src/compiler/common/plugins/indexage/_utils/Stringify.ts +72 -0
- package/src/compiler/common/plugins/indexage/_utils/annotations.ts +88 -0
- package/src/compiler/common/plugins/indexage/_utils/iterateur.ts +52 -0
- package/src/compiler/common/plugins/indexage/icones-svg/index.ts +198 -0
- package/src/compiler/common/plugins/indexage/index.ts +132 -0
- package/src/compiler/common/plugins/indexage/indexeur.ts +13 -0
- package/src/compiler/common/plugins/indexage/injection-dependances/index.ts +68 -0
- package/src/compiler/index.ts +86 -0
- package/src/compiler/server/index.ts +177 -0
- package/src/index.ts +192 -0
- package/src/paths.ts +158 -0
- package/src/print.ts +12 -0
- package/src/utils/index.ts +22 -0
- package/src/utils/keyboard.ts +78 -0
- package/tsconfig.json +38 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
|
|
2
|
+
import * as types from '@babel/types'
|
|
3
|
+
import generate from "@babel/generator";
|
|
4
|
+
|
|
5
|
+
const servicesViaContexte: {
|
|
6
|
+
[nomService: string]: string
|
|
7
|
+
} = {
|
|
8
|
+
User: 'user',
|
|
9
|
+
|
|
10
|
+
Request: 'req',
|
|
11
|
+
Response: 'res',
|
|
12
|
+
NextFunction: 'next'
|
|
13
|
+
} as const
|
|
14
|
+
|
|
15
|
+
const debug = false
|
|
16
|
+
|
|
17
|
+
export default (t: typeof types) => {
|
|
18
|
+
|
|
19
|
+
function remplacerFonction(arg: types.ArrowFunctionExpression): null | types.ArrowFunctionExpression;
|
|
20
|
+
function remplacerFonction(arg: types.ClassMethod): null | types.ClassMethod;
|
|
21
|
+
function remplacerFonction(
|
|
22
|
+
arg: types.ArrowFunctionExpression | types.ClassMethod
|
|
23
|
+
): null | types.ArrowFunctionExpression | types.ClassMethod {
|
|
24
|
+
|
|
25
|
+
const dejaTraite = (
|
|
26
|
+
arg.params.length === 1
|
|
27
|
+
&&
|
|
28
|
+
arg.params[0].type === 'RestElement'
|
|
29
|
+
)
|
|
30
|
+
if (dejaTraite)
|
|
31
|
+
return null;
|
|
32
|
+
|
|
33
|
+
// Return null = inchangé
|
|
34
|
+
if (arg.body.type !== 'BlockStatement')
|
|
35
|
+
return null;
|
|
36
|
+
|
|
37
|
+
let declarations: types.VariableDeclarator[] = [];
|
|
38
|
+
let instanciations: types.ExpressionStatement[] = [];
|
|
39
|
+
let extractions: types.ExpressionStatement[] = [];
|
|
40
|
+
|
|
41
|
+
// Transforme les paramètres en une liste d'instanciations
|
|
42
|
+
for (let iParam = 0; iParam < arg.params.length; iParam++) {
|
|
43
|
+
const param = arg.params[iParam];
|
|
44
|
+
|
|
45
|
+
// utilisateur: User
|
|
46
|
+
if (
|
|
47
|
+
param.type === 'Identifier' && param.typeAnnotation
|
|
48
|
+
&&
|
|
49
|
+
param.typeAnnotation.type === 'TSTypeAnnotation'
|
|
50
|
+
&&
|
|
51
|
+
param.typeAnnotation.typeAnnotation.type === 'TSTypeReference'
|
|
52
|
+
&&
|
|
53
|
+
param.typeAnnotation.typeAnnotation.typeName.type === 'Identifier'
|
|
54
|
+
) {
|
|
55
|
+
|
|
56
|
+
const typeParam = param.typeAnnotation.typeAnnotation.typeName.name;
|
|
57
|
+
const nomParam = param.name;
|
|
58
|
+
|
|
59
|
+
// Déclaration
|
|
60
|
+
declarations.push(
|
|
61
|
+
t.variableDeclarator(
|
|
62
|
+
t.identifier(nomParam)
|
|
63
|
+
)
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// Instanciation (quand args[0] = contexte)
|
|
67
|
+
if (typeParam in servicesViaContexte) {
|
|
68
|
+
|
|
69
|
+
// <nomParam> = args[ 0 ][ servicesViaContexte[ typeParam ] ];
|
|
70
|
+
instanciations.push(
|
|
71
|
+
t.expressionStatement(
|
|
72
|
+
t.assignmentExpression(
|
|
73
|
+
'=',
|
|
74
|
+
t.identifier(nomParam),
|
|
75
|
+
t.memberExpression(
|
|
76
|
+
t.memberExpression(
|
|
77
|
+
t.identifier('args'),
|
|
78
|
+
t.numericLiteral(0),
|
|
79
|
+
true
|
|
80
|
+
),
|
|
81
|
+
t.identifier(servicesViaContexte[typeParam])
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
// Le type du paramètre est un service reconnu
|
|
88
|
+
} else if (this.servicesImportes[typeParam] !== undefined) {
|
|
89
|
+
|
|
90
|
+
// <nomParam> = new <typeParam>( args[0] );
|
|
91
|
+
instanciations.push(
|
|
92
|
+
t.expressionStatement(
|
|
93
|
+
t.assignmentExpression(
|
|
94
|
+
'=',
|
|
95
|
+
t.identifier(nomParam),
|
|
96
|
+
t.newExpression(
|
|
97
|
+
t.identifier(typeParam),
|
|
98
|
+
[
|
|
99
|
+
t.memberExpression(
|
|
100
|
+
t.identifier('args'),
|
|
101
|
+
t.numericLiteral(0),
|
|
102
|
+
true
|
|
103
|
+
)
|
|
104
|
+
]
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
} else {
|
|
111
|
+
console.log(this.servicesImportes);
|
|
112
|
+
throw new Error(`Impossible de trouver l'import associée au type portant pour nom ${typeParam} (fichier: ${this.fichier}). Liste des iportations trouvées au dessus.`)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Extractions
|
|
116
|
+
// <nomParam> = args[ <index> ]
|
|
117
|
+
extractions.push(
|
|
118
|
+
t.expressionStatement(
|
|
119
|
+
t.assignmentExpression(
|
|
120
|
+
'=',
|
|
121
|
+
t.identifier(nomParam),
|
|
122
|
+
t.memberExpression(
|
|
123
|
+
t.identifier('args'),
|
|
124
|
+
t.identifier( iParam.toString() ),
|
|
125
|
+
true
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Inchangé
|
|
135
|
+
if (instanciations.length === 0)
|
|
136
|
+
return null;
|
|
137
|
+
|
|
138
|
+
const conditionSiDoitInstancier = (
|
|
139
|
+
t.logicalExpression(
|
|
140
|
+
'&&',
|
|
141
|
+
t.logicalExpression(
|
|
142
|
+
'&&',
|
|
143
|
+
|
|
144
|
+
// args[0] !== undefined
|
|
145
|
+
t.binaryExpression(
|
|
146
|
+
'!==',
|
|
147
|
+
t.memberExpression(
|
|
148
|
+
t.identifier('args'),
|
|
149
|
+
t.numericLiteral(0),
|
|
150
|
+
true
|
|
151
|
+
),
|
|
152
|
+
t.identifier('undefined')
|
|
153
|
+
),
|
|
154
|
+
|
|
155
|
+
// typeof args[0] === 'object'
|
|
156
|
+
t.binaryExpression(
|
|
157
|
+
'===',
|
|
158
|
+
t.unaryExpression(
|
|
159
|
+
'typeof',
|
|
160
|
+
t.memberExpression(
|
|
161
|
+
t.identifier('args'),
|
|
162
|
+
t.identifier('0'),
|
|
163
|
+
true
|
|
164
|
+
)
|
|
165
|
+
),
|
|
166
|
+
t.stringLiteral('object')
|
|
167
|
+
)
|
|
168
|
+
),
|
|
169
|
+
|
|
170
|
+
// args[0].type === 'contexte_requete'
|
|
171
|
+
t.binaryExpression(
|
|
172
|
+
'===',
|
|
173
|
+
t.memberExpression(
|
|
174
|
+
t.memberExpression(
|
|
175
|
+
t.identifier('args'),
|
|
176
|
+
t.identifier('0'),
|
|
177
|
+
true
|
|
178
|
+
),
|
|
179
|
+
t.identifier('type')
|
|
180
|
+
),
|
|
181
|
+
t.stringLiteral('contexte_requete')
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
const body = t.blockStatement([
|
|
187
|
+
t.variableDeclaration('let', declarations),
|
|
188
|
+
|
|
189
|
+
// if (args[0] !== undefined && typeof args[0] === 'object' && args[0].type === 'contexte_requete')
|
|
190
|
+
t.ifStatement(
|
|
191
|
+
conditionSiDoitInstancier,
|
|
192
|
+
|
|
193
|
+
t.blockStatement(instanciations),
|
|
194
|
+
|
|
195
|
+
t.blockStatement(extractions)
|
|
196
|
+
),
|
|
197
|
+
...arg.body.body
|
|
198
|
+
]);
|
|
199
|
+
|
|
200
|
+
// Remplacement paramètres et ajout des instanciations
|
|
201
|
+
const remplacement = arg.type === 'ArrowFunctionExpression'
|
|
202
|
+
? t.arrowFunctionExpression(
|
|
203
|
+
[t.restElement( t.identifier('args') )],
|
|
204
|
+
body,
|
|
205
|
+
arg.async
|
|
206
|
+
)
|
|
207
|
+
: t.classMethod(
|
|
208
|
+
'constructor',
|
|
209
|
+
t.identifier('constructor'),
|
|
210
|
+
[t.restElement( t.identifier('args') )],
|
|
211
|
+
body
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
if (debug) {
|
|
215
|
+
console.log('-----------------------------');
|
|
216
|
+
console.log(generate(arg).code);
|
|
217
|
+
console.log('=>');
|
|
218
|
+
console.log(generate(remplacement).code);
|
|
219
|
+
console.log('-----------------------------');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return remplacement;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return remplacerFonction;
|
|
226
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
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 { TAppSide } from '@cli/index';
|
|
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 t = babel.types as typeof types;
|
|
37
|
+
let program: NodePath<types.Program>;
|
|
38
|
+
|
|
39
|
+
const plugin: PluginObj<{
|
|
40
|
+
filename: string
|
|
41
|
+
}> = {
|
|
42
|
+
pre(state) {
|
|
43
|
+
|
|
44
|
+
this.filename = state.opts.filename as string;
|
|
45
|
+
|
|
46
|
+
},
|
|
47
|
+
visitor: {
|
|
48
|
+
|
|
49
|
+
Program(path) {
|
|
50
|
+
program = path;
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
CallExpression(path) {
|
|
54
|
+
|
|
55
|
+
// route.xxx( <PATH>, ... )
|
|
56
|
+
if (!(
|
|
57
|
+
path.node.callee.type === 'MemberExpression'
|
|
58
|
+
&&
|
|
59
|
+
path.node.callee.object.type === "Identifier"
|
|
60
|
+
&&
|
|
61
|
+
path.node.callee.object.name === "route"
|
|
62
|
+
&&
|
|
63
|
+
path.node.arguments.length >= 2
|
|
64
|
+
))
|
|
65
|
+
return;
|
|
66
|
+
|
|
67
|
+
const [routePath, ...routeArgs] = path.node.arguments;
|
|
68
|
+
debug && console.log(`[routes]`, this.filename, ...routeArgs.map(n => n.type));
|
|
69
|
+
|
|
70
|
+
// Inject chunk id in options (2nd arg)
|
|
71
|
+
const status = addChunkId(routeArgs, this.filename);
|
|
72
|
+
if (status === 'ALREADY_PROCESSED')
|
|
73
|
+
return;
|
|
74
|
+
|
|
75
|
+
// Transform 2nd arg of renderer to a useContext spread
|
|
76
|
+
addRendererContext(routeArgs, this.filename);
|
|
77
|
+
|
|
78
|
+
const replacement = t.callExpression( path.node.callee, [ routePath, ...routeArgs ]);
|
|
79
|
+
debug && console.log( generate(replacement).code );
|
|
80
|
+
|
|
81
|
+
// Force export default
|
|
82
|
+
if (path.parent.type === 'ExportDefaultDeclaration')
|
|
83
|
+
path.replaceWith( replacement );
|
|
84
|
+
else
|
|
85
|
+
path.parentPath.replaceWith(
|
|
86
|
+
t.exportDefaultDeclaration( replacement )
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
function addChunkId(
|
|
95
|
+
routeArgs: types.CallExpression["arguments"],
|
|
96
|
+
filename: string
|
|
97
|
+
): void | 'ALREADY_PROCESSED' {
|
|
98
|
+
|
|
99
|
+
if (routeArgs[0].type === 'ObjectExpression') {
|
|
100
|
+
|
|
101
|
+
if (routeArgs[0].properties.some(o =>
|
|
102
|
+
o.type === 'ObjectProperty'
|
|
103
|
+
&&
|
|
104
|
+
o.key.type === 'Identifier'
|
|
105
|
+
&&
|
|
106
|
+
o.key.name === 'id'
|
|
107
|
+
)) {
|
|
108
|
+
debug && console.log(`[routes]`, filename, 'Already Processed');
|
|
109
|
+
return 'ALREADY_PROCESSED';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
} else
|
|
113
|
+
routeArgs.unshift(t.objectExpression([]));
|
|
114
|
+
|
|
115
|
+
const { filepath, chunkId } = cli.paths.getPageChunk(filename);
|
|
116
|
+
debug && console.log(`[routes]`, filename, '=>', chunkId);
|
|
117
|
+
|
|
118
|
+
// Add object property
|
|
119
|
+
(routeArgs[0] as types.ObjectExpression).properties.push(
|
|
120
|
+
t.objectProperty(
|
|
121
|
+
t.identifier('id'),
|
|
122
|
+
t.stringLiteral(chunkId)
|
|
123
|
+
),
|
|
124
|
+
t.objectProperty(
|
|
125
|
+
t.identifier('filepath'),
|
|
126
|
+
t.stringLiteral(filepath)
|
|
127
|
+
)
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function addRendererContext(
|
|
133
|
+
routeArgs: types.CallExpression["arguments"],
|
|
134
|
+
filename: string
|
|
135
|
+
) {
|
|
136
|
+
|
|
137
|
+
// ( <data>, { response, api }) => ....
|
|
138
|
+
const renderer = routeArgs[ routeArgs.length - 1 ];
|
|
139
|
+
if (!(
|
|
140
|
+
renderer.type === 'ArrowFunctionExpression'
|
|
141
|
+
&&
|
|
142
|
+
renderer.params.length > 1
|
|
143
|
+
))
|
|
144
|
+
return;
|
|
145
|
+
|
|
146
|
+
// Remove 2nd arg (renderer = react component, so only 1 arg for props)
|
|
147
|
+
const declaration = renderer.params.pop() as types.ArrowFunctionExpression["params"][number];
|
|
148
|
+
// const <param2> = useContext();
|
|
149
|
+
const ctxDeclaration = t.variableDeclaration('const', [t.variableDeclarator(
|
|
150
|
+
declaration,
|
|
151
|
+
t.callExpression(t.identifier('useContext'), [])
|
|
152
|
+
)])
|
|
153
|
+
|
|
154
|
+
// Add Declaration
|
|
155
|
+
switch (renderer.body.type) {
|
|
156
|
+
case 'BlockStatement':
|
|
157
|
+
renderer.body.body.unshift(ctxDeclaration);
|
|
158
|
+
break;
|
|
159
|
+
// TODO: Si type === JSXElement, remplacer par BlockStatement (ctxDeclaration + return JSX)
|
|
160
|
+
/*case 'BlockStatement':
|
|
161
|
+
renderer.re
|
|
162
|
+
break;*/
|
|
163
|
+
default:
|
|
164
|
+
throw new Error(`Unknown body type for the renderer: ${renderer.body.type}`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Add usecontext import if it doesn't exists
|
|
168
|
+
if (program.scope.bindings["useContext"] === undefined) {
|
|
169
|
+
|
|
170
|
+
debug && console.log(`[routes]`, filename, `Adding useContext import from @context`);
|
|
171
|
+
|
|
172
|
+
program.unshiftContainer(
|
|
173
|
+
'body',
|
|
174
|
+
t.importDeclaration(
|
|
175
|
+
[t.importDefaultSpecifier(t.identifier('useContext'))],
|
|
176
|
+
t.stringLiteral('@client/context')
|
|
177
|
+
)
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return plugin;
|
|
185
|
+
}
|