5htp 0.6.3 → 0.6.4
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/app/index.ts +8 -2
- package/cli.js +13 -0
- package/commands/build.ts +35 -8
- package/commands/refresh.ts +18 -0
- package/compiler/client/index.ts +5 -7
- package/compiler/common/babel/index.ts +0 -10
- package/compiler/common/babel/plugins/services.ts +263 -13
- package/compiler/common/babel/routes/routes.ts +500 -13
- package/compiler/common/index.ts +9 -13
- package/compiler/index.ts +32 -17
- package/compiler/server/index.ts +6 -3
- package/index.ts +5 -3
- package/package.json +3 -4
- package/readme.md +14 -2
- package/compiler/common/babel/plugins/form.ts +0 -191
- package/compiler/common/babel/plugins/icones-svg.ts +0 -376
- package/compiler/common/babel/plugins/injection-dependances/index.ts +0 -225
- package/compiler/common/babel/plugins/injection-dependances/remplacerFonction.ts +0 -226
- package/compiler/common/babel/plugins/queries/index.ts +0 -166
- package/compiler/common/plugins/indexage/_utils/Stringify.ts +0 -72
- package/compiler/common/plugins/indexage/_utils/annotations.ts +0 -88
- package/compiler/common/plugins/indexage/_utils/iterateur.ts +0 -52
- package/compiler/common/plugins/indexage/icones-svg/index.ts +0 -207
- package/compiler/common/plugins/indexage/index.ts +0 -134
- package/compiler/common/plugins/indexage/indexeur.ts +0 -13
- package/compiler/common/plugins/indexage/injection-dependances/index.ts +0 -68
package/app/index.ts
CHANGED
|
@@ -56,7 +56,13 @@ export class App {
|
|
|
56
56
|
public paths = {
|
|
57
57
|
|
|
58
58
|
root: cli.paths.appRoot,
|
|
59
|
-
|
|
59
|
+
binBuild: path.join( cli.paths.appRoot, 'bin'),
|
|
60
|
+
binDev: path.join( cli.paths.appRoot, 'bin-dev'),
|
|
61
|
+
get bin() {
|
|
62
|
+
return cli.commandName === 'dev'
|
|
63
|
+
? this.binDev
|
|
64
|
+
: this.binBuild;
|
|
65
|
+
},
|
|
60
66
|
data: path.join( cli.paths.appRoot, 'var', 'data'),
|
|
61
67
|
public: path.join( cli.paths.appRoot, 'public'),
|
|
62
68
|
pages: path.join( cli.paths.appRoot, 'client', 'pages'),
|
|
@@ -192,4 +198,4 @@ export class App {
|
|
|
192
198
|
|
|
193
199
|
export const app = new App
|
|
194
200
|
|
|
195
|
-
export default app
|
|
201
|
+
export default app
|
package/cli.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
console.log('=== Running 5HTP via ts-node ===');
|
|
4
|
+
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const tsNode = require('ts-node');
|
|
7
|
+
|
|
8
|
+
tsNode.register({
|
|
9
|
+
project: path.join(__dirname, 'tsconfig.json'),
|
|
10
|
+
transpileOnly: true,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
require('./index.ts');
|
package/commands/build.ts
CHANGED
|
@@ -2,22 +2,35 @@
|
|
|
2
2
|
- DEPENDANCES
|
|
3
3
|
----------------------------------*/
|
|
4
4
|
|
|
5
|
-
// Npm
|
|
6
|
-
import prompts from 'prompts';
|
|
7
|
-
|
|
8
5
|
// Configs
|
|
6
|
+
import cli from '..';
|
|
9
7
|
import Compiler from '../compiler';
|
|
8
|
+
import type { TCompileMode } from '../compiler/common';
|
|
10
9
|
|
|
11
10
|
/*----------------------------------
|
|
12
11
|
- TYPES
|
|
13
12
|
----------------------------------*/
|
|
14
13
|
|
|
14
|
+
const buildModes: TCompileMode[] = ['dev', 'prod'];
|
|
15
|
+
|
|
16
|
+
function getBuildMode(): TCompileMode {
|
|
17
|
+
|
|
18
|
+
const selectedModes = buildModes.filter(mode => cli.args[mode] === true);
|
|
19
|
+
|
|
20
|
+
if (selectedModes.length > 1)
|
|
21
|
+
throw new Error(`Build mode is ambiguous. Use only one mode among: ${buildModes.join(', ')}`);
|
|
22
|
+
|
|
23
|
+
return selectedModes[0] || 'dev';
|
|
24
|
+
}
|
|
25
|
+
|
|
15
26
|
/*----------------------------------
|
|
16
27
|
- COMMAND
|
|
17
28
|
----------------------------------*/
|
|
18
|
-
export const run = (): Promise<void> => new Promise(async (resolve) => {
|
|
29
|
+
export const run = (): Promise<void> => new Promise(async (resolve, reject) => {
|
|
19
30
|
|
|
20
|
-
const
|
|
31
|
+
const mode = getBuildMode();
|
|
32
|
+
|
|
33
|
+
const compiler = new Compiler(mode);
|
|
21
34
|
|
|
22
35
|
const multiCompiler = await compiler.create();
|
|
23
36
|
|
|
@@ -25,10 +38,24 @@ export const run = (): Promise<void> => new Promise(async (resolve) => {
|
|
|
25
38
|
|
|
26
39
|
if (error) {
|
|
27
40
|
console.error("An error occurred during the compilation:", error);
|
|
28
|
-
|
|
41
|
+
reject(error);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (stats?.hasErrors()) {
|
|
46
|
+
reject(new Error(`Build failed in ${mode} mode.`));
|
|
47
|
+
return;
|
|
29
48
|
}
|
|
30
49
|
|
|
31
|
-
|
|
50
|
+
multiCompiler.close((closeError) => {
|
|
51
|
+
|
|
52
|
+
if (closeError) {
|
|
53
|
+
reject(closeError);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
resolve();
|
|
58
|
+
});
|
|
32
59
|
|
|
33
60
|
});
|
|
34
|
-
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Configs
|
|
6
|
+
import Compiler from '../compiler';
|
|
7
|
+
|
|
8
|
+
/*----------------------------------
|
|
9
|
+
- COMMAND
|
|
10
|
+
----------------------------------*/
|
|
11
|
+
export const run = (): Promise<void> => new Promise(async (resolve) => {
|
|
12
|
+
|
|
13
|
+
const compiler = new Compiler('dev');
|
|
14
|
+
|
|
15
|
+
await compiler.refreshGeneratedTypings();
|
|
16
|
+
|
|
17
|
+
resolve();
|
|
18
|
+
});
|
package/compiler/client/index.ts
CHANGED
|
@@ -11,9 +11,6 @@ const TerserPlugin = require('terser-webpack-plugin');
|
|
|
11
11
|
// Optimisations
|
|
12
12
|
const BrotliCompression = require("brotli-webpack-plugin");
|
|
13
13
|
import CompressionPlugin from "compression-webpack-plugin";
|
|
14
|
-
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
|
|
15
|
-
const imageminWebp = require('imagemin-webp');
|
|
16
|
-
const { extendDefaultPlugins } = require("svgo");
|
|
17
14
|
// Ressources
|
|
18
15
|
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
|
|
19
16
|
import MiniCssExtractPlugin from "mini-css-extract-plugin";
|
|
@@ -38,6 +35,7 @@ export default function createCompiler( app: App, mode: TCompileMode ): webpack.
|
|
|
38
35
|
|
|
39
36
|
console.info(`Creating compiler for client (${mode}).`);
|
|
40
37
|
const dev = mode === 'dev';
|
|
38
|
+
const buildCheck = dev && cli.commandName === 'build';
|
|
41
39
|
|
|
42
40
|
const commonConfig = createCommonConfig(app, 'client', mode);
|
|
43
41
|
|
|
@@ -81,7 +79,7 @@ export default function createCompiler( app: App, mode: TCompileMode ): webpack.
|
|
|
81
79
|
|
|
82
80
|
output: {
|
|
83
81
|
|
|
84
|
-
pathinfo: dev,
|
|
82
|
+
pathinfo: dev && !buildCheck,
|
|
85
83
|
path: app.paths.bin + '/public',
|
|
86
84
|
filename: '[name].js', // Output client.js
|
|
87
85
|
assetModuleFilename: '[hash][ext]',
|
|
@@ -160,7 +158,7 @@ export default function createCompiler( app: App, mode: TCompileMode ): webpack.
|
|
|
160
158
|
// Emit a file with assets cli.paths
|
|
161
159
|
// https://github.com/webdeveric/webpack-assets-manifest#options
|
|
162
160
|
new WebpackAssetsManifest({
|
|
163
|
-
output: app.paths.
|
|
161
|
+
output: app.paths.bin + `/asset-manifest.json`,
|
|
164
162
|
publicPath: true,
|
|
165
163
|
writeToDisk: true, // Force la copie du fichier sur e disque, au lieu d'en mémoire en mode dev
|
|
166
164
|
customize: ({ key, value }) => {
|
|
@@ -170,7 +168,7 @@ export default function createCompiler( app: App, mode: TCompileMode ): webpack.
|
|
|
170
168
|
},
|
|
171
169
|
done: (manifest, stats) => {
|
|
172
170
|
// Write chunk-manifest.json.json
|
|
173
|
-
const chunkFileName = app.paths.
|
|
171
|
+
const chunkFileName = app.paths.bin + `/chunk-manifest.json`;
|
|
174
172
|
try {
|
|
175
173
|
const fileFilter = file => !file.endsWith('.map');
|
|
176
174
|
const addPath = file => manifest.getPublicPath(file);
|
|
@@ -229,7 +227,7 @@ export default function createCompiler( app: App, mode: TCompileMode ): webpack.
|
|
|
229
227
|
],
|
|
230
228
|
|
|
231
229
|
// https://webpack.js.org/configuration/devtool/#devtool
|
|
232
|
-
devtool: 'source-map',
|
|
230
|
+
devtool: buildCheck ? false : 'source-map',
|
|
233
231
|
/*devServer: {
|
|
234
232
|
hot: true,
|
|
235
233
|
},*/
|
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
// Npm
|
|
6
6
|
import type webpack from 'webpack';
|
|
7
7
|
import PresetBabel, { Options } from '@babel/preset-env';
|
|
8
|
-
// Core
|
|
9
|
-
import PluginIndexage from '../plugins/indexage';
|
|
10
8
|
|
|
11
9
|
import cli from '@cli';
|
|
12
10
|
import type { TAppSide, App } from '@cli/app';
|
|
@@ -70,10 +68,6 @@ module.exports = (app: App, side: TAppSide, dev: boolean): webpack.RuleSetRule[]
|
|
|
70
68
|
// Désactive car ralenti compilation
|
|
71
69
|
cacheCompression: false,
|
|
72
70
|
|
|
73
|
-
metadataSubscribers: [
|
|
74
|
-
PluginIndexage.metadataContextFunctionName
|
|
75
|
-
],
|
|
76
|
-
|
|
77
71
|
compact: !dev,
|
|
78
72
|
|
|
79
73
|
// https://babeljs.io/docs/usage/options/
|
|
@@ -165,14 +159,10 @@ module.exports = (app: App, side: TAppSide, dev: boolean): webpack.RuleSetRule[]
|
|
|
165
159
|
],
|
|
166
160
|
|
|
167
161
|
overrides: [
|
|
168
|
-
|
|
169
|
-
require('./plugins/icones-svg')(app),
|
|
170
162
|
|
|
171
163
|
...(side === 'client' ? [
|
|
172
164
|
|
|
173
165
|
] : [
|
|
174
|
-
//require('./plugins/queries'),
|
|
175
|
-
//require('./plugins/injection-dependances'),
|
|
176
166
|
]),
|
|
177
167
|
]
|
|
178
168
|
}
|
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
import * as types from '@babel/types'
|
|
7
7
|
import type { NodePath, PluginObj } from '@babel/core';
|
|
8
8
|
import generate from '@babel/generator';
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { parse } from '@babel/parser';
|
|
12
|
+
import traverse from '@babel/traverse';
|
|
9
13
|
|
|
10
14
|
// Core
|
|
11
15
|
import cli from '@cli';
|
|
@@ -38,6 +42,170 @@ type TImportedIndex = {
|
|
|
38
42
|
source: TImportSource // container | application | models
|
|
39
43
|
}
|
|
40
44
|
|
|
45
|
+
type TRoutesIndex = {
|
|
46
|
+
byFile: Map<string, Set<string>>,
|
|
47
|
+
all: Set<string>,
|
|
48
|
+
initialized: boolean
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const routesIndexByAppRoot = new Map<string, TRoutesIndex>();
|
|
52
|
+
|
|
53
|
+
function isRouteDecoratorExpression(expression: types.Expression): boolean {
|
|
54
|
+
return (
|
|
55
|
+
// Handles the case of @Route without parameters
|
|
56
|
+
(types.isIdentifier(expression) && expression.name === 'Route')
|
|
57
|
+
||
|
|
58
|
+
// Handles the case of @Route() with parameters
|
|
59
|
+
(
|
|
60
|
+
types.isCallExpression(expression)
|
|
61
|
+
&&
|
|
62
|
+
types.isIdentifier(expression.callee)
|
|
63
|
+
&&
|
|
64
|
+
expression.callee.name === 'Route'
|
|
65
|
+
)
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function getRoutePathFromDecoratorExpression(expression: types.Expression): string | undefined {
|
|
70
|
+
if (
|
|
71
|
+
!types.isCallExpression(expression)
|
|
72
|
+
||
|
|
73
|
+
!types.isIdentifier(expression.callee)
|
|
74
|
+
||
|
|
75
|
+
expression.callee.name !== 'Route'
|
|
76
|
+
)
|
|
77
|
+
return;
|
|
78
|
+
|
|
79
|
+
const firstArg = expression.arguments[0];
|
|
80
|
+
if (!types.isStringLiteral(firstArg))
|
|
81
|
+
return;
|
|
82
|
+
|
|
83
|
+
return firstArg.value;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function extractRoutePathsFromCode(code: string, filename: string): Set<string> {
|
|
87
|
+
const routePaths = new Set<string>();
|
|
88
|
+
|
|
89
|
+
let ast: ReturnType<typeof parse> | undefined;
|
|
90
|
+
try {
|
|
91
|
+
ast = parse(code, {
|
|
92
|
+
sourceType: 'module',
|
|
93
|
+
sourceFilename: filename,
|
|
94
|
+
plugins: [
|
|
95
|
+
'typescript',
|
|
96
|
+
'decorators-legacy',
|
|
97
|
+
'jsx',
|
|
98
|
+
'classProperties',
|
|
99
|
+
'classPrivateProperties',
|
|
100
|
+
'classPrivateMethods',
|
|
101
|
+
'dynamicImport',
|
|
102
|
+
'importMeta',
|
|
103
|
+
'optionalChaining',
|
|
104
|
+
'nullishCoalescingOperator',
|
|
105
|
+
'topLevelAwait',
|
|
106
|
+
],
|
|
107
|
+
});
|
|
108
|
+
} catch {
|
|
109
|
+
return routePaths;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
traverse(ast, {
|
|
113
|
+
ClassMethod(path) {
|
|
114
|
+
const { node } = path;
|
|
115
|
+
if (!node.decorators || node.key.type !== 'Identifier')
|
|
116
|
+
return;
|
|
117
|
+
|
|
118
|
+
for (const decorator of node.decorators) {
|
|
119
|
+
if (!isRouteDecoratorExpression(decorator.expression))
|
|
120
|
+
continue;
|
|
121
|
+
|
|
122
|
+
const routePath = getRoutePathFromDecoratorExpression(decorator.expression);
|
|
123
|
+
if (routePath)
|
|
124
|
+
routePaths.add(routePath);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return routePaths;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function listTsFiles(dirPath: string): string[] {
|
|
133
|
+
let entries: fs.Dirent[];
|
|
134
|
+
try {
|
|
135
|
+
entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
136
|
+
} catch {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const files: string[] = [];
|
|
141
|
+
for (const entry of entries) {
|
|
142
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
143
|
+
if (entry.isDirectory()) {
|
|
144
|
+
files.push(...listTsFiles(fullPath));
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const isTs = fullPath.endsWith('.ts') || fullPath.endsWith('.tsx');
|
|
149
|
+
if (!isTs || fullPath.endsWith('.d.ts'))
|
|
150
|
+
continue;
|
|
151
|
+
|
|
152
|
+
files.push(fullPath);
|
|
153
|
+
}
|
|
154
|
+
return files;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function initializeRoutesIndex(appRoot: string, index: TRoutesIndex) {
|
|
158
|
+
const servicesDir = path.join(appRoot, 'server', 'services');
|
|
159
|
+
const files = listTsFiles(servicesDir);
|
|
160
|
+
|
|
161
|
+
for (const file of files) {
|
|
162
|
+
let code: string;
|
|
163
|
+
try {
|
|
164
|
+
code = fs.readFileSync(file, 'utf8');
|
|
165
|
+
} catch {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const routes = extractRoutePathsFromCode(code, file);
|
|
170
|
+
index.byFile.set(file, routes);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
index.all.clear();
|
|
174
|
+
for (const routes of index.byFile.values()) {
|
|
175
|
+
for (const route of routes)
|
|
176
|
+
index.all.add(route);
|
|
177
|
+
}
|
|
178
|
+
index.initialized = true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function getRoutesIndex(appRoot: string): TRoutesIndex {
|
|
182
|
+
let index = routesIndexByAppRoot.get(appRoot);
|
|
183
|
+
if (!index) {
|
|
184
|
+
index = {
|
|
185
|
+
byFile: new Map(),
|
|
186
|
+
all: new Set(),
|
|
187
|
+
initialized: false
|
|
188
|
+
};
|
|
189
|
+
routesIndexByAppRoot.set(appRoot, index);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (!index.initialized) {
|
|
193
|
+
initializeRoutesIndex(appRoot, index);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return index;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function updateRoutesIndexForFile(index: TRoutesIndex, filename: string, routePaths: Set<string>) {
|
|
200
|
+
index.byFile.set(filename, routePaths);
|
|
201
|
+
|
|
202
|
+
index.all.clear();
|
|
203
|
+
for (const routes of index.byFile.values()) {
|
|
204
|
+
for (const route of routes)
|
|
205
|
+
index.all.add(route);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
41
209
|
/*----------------------------------
|
|
42
210
|
- PLUGIN
|
|
43
211
|
----------------------------------*/
|
|
@@ -75,6 +243,9 @@ function Plugin(babel, { app, side, debug }: TOptions) {
|
|
|
75
243
|
// Count how many total imports we transform
|
|
76
244
|
importedCount: number,
|
|
77
245
|
routeMethods: string[],
|
|
246
|
+
routePaths: Set<string>,
|
|
247
|
+
routesIndex: TRoutesIndex,
|
|
248
|
+
contextGuardedClassMethods: WeakSet<types.ClassMethod>,
|
|
78
249
|
|
|
79
250
|
// For every local identifier, store info about how it should be rewritten
|
|
80
251
|
imported: {
|
|
@@ -92,6 +263,10 @@ function Plugin(babel, { app, side, debug }: TOptions) {
|
|
|
92
263
|
this.debug = debug || false;
|
|
93
264
|
|
|
94
265
|
this.routeMethods = [];
|
|
266
|
+
this.routePaths = new Set();
|
|
267
|
+
|
|
268
|
+
this.routesIndex = getRoutesIndex(app.paths.root);
|
|
269
|
+
this.contextGuardedClassMethods = new WeakSet();
|
|
95
270
|
},
|
|
96
271
|
|
|
97
272
|
visitor: {
|
|
@@ -109,28 +284,21 @@ function Plugin(babel, { app, side, debug }: TOptions) {
|
|
|
109
284
|
|
|
110
285
|
for (const decorator of node.decorators) {
|
|
111
286
|
|
|
112
|
-
const isRoute = (
|
|
113
|
-
// Handles the case of @Route without parameters
|
|
114
|
-
(
|
|
115
|
-
t.isIdentifier(decorator.expression) && decorator.expression.name === 'Route'
|
|
116
|
-
)
|
|
117
|
-
||
|
|
118
|
-
// Handles the case of @Route() with parameters
|
|
119
|
-
(
|
|
120
|
-
t.isCallExpression(decorator.expression) &&
|
|
121
|
-
t.isIdentifier(decorator.expression.callee) &&
|
|
122
|
-
decorator.expression.callee.name === 'Route'
|
|
123
|
-
)
|
|
124
|
-
);
|
|
287
|
+
const isRoute = isRouteDecoratorExpression(decorator.expression);
|
|
125
288
|
|
|
126
289
|
if (!isRoute) continue;
|
|
127
290
|
|
|
128
291
|
const methodName = node.key.name;
|
|
129
292
|
this.routeMethods.push( methodName );
|
|
130
293
|
|
|
294
|
+
const routePath = getRoutePathFromDecoratorExpression(decorator.expression);
|
|
295
|
+
if (routePath)
|
|
296
|
+
this.routePaths.add(routePath);
|
|
131
297
|
}
|
|
132
298
|
}
|
|
133
299
|
});
|
|
300
|
+
|
|
301
|
+
updateRoutesIndexForFile(this.routesIndex, this.filename, this.routePaths);
|
|
134
302
|
},
|
|
135
303
|
|
|
136
304
|
/**
|
|
@@ -203,6 +371,88 @@ function Plugin(babel, { app, side, debug }: TOptions) {
|
|
|
203
371
|
path.replaceWithMultiple(replaceWith);
|
|
204
372
|
},
|
|
205
373
|
|
|
374
|
+
CallExpression(path) {
|
|
375
|
+
if (!this.processFile)
|
|
376
|
+
return;
|
|
377
|
+
|
|
378
|
+
const classMethodPath = path.findParent(p => p.isClassMethod()) as NodePath<types.ClassMethod> | null;
|
|
379
|
+
if (!classMethodPath)
|
|
380
|
+
return;
|
|
381
|
+
|
|
382
|
+
// Ignore constructors
|
|
383
|
+
if (classMethodPath.node.kind === 'constructor')
|
|
384
|
+
return;
|
|
385
|
+
|
|
386
|
+
const callee = path.node.callee;
|
|
387
|
+
if (!t.isMemberExpression(callee) && !(t as any).isOptionalMemberExpression?.(callee))
|
|
388
|
+
return;
|
|
389
|
+
|
|
390
|
+
// Build member chain segments: this.app.<Service>.<...>
|
|
391
|
+
const segments: string[] = [];
|
|
392
|
+
let current: any = callee;
|
|
393
|
+
while (t.isMemberExpression(current) || (t as any).isOptionalMemberExpression?.(current)) {
|
|
394
|
+
const prop = current.property;
|
|
395
|
+
if (t.isIdentifier(prop)) {
|
|
396
|
+
segments.unshift(prop.name);
|
|
397
|
+
} else if (t.isStringLiteral(prop)) {
|
|
398
|
+
segments.unshift(prop.value);
|
|
399
|
+
} else {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
current = current.object;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (!t.isThisExpression(current))
|
|
406
|
+
return;
|
|
407
|
+
|
|
408
|
+
// Expect: this.app.<Service>.<method>
|
|
409
|
+
if (segments.length < 3 || segments[0] !== 'app')
|
|
410
|
+
return;
|
|
411
|
+
|
|
412
|
+
const serviceLocalName = segments[1];
|
|
413
|
+
const importedRef = this.imported[serviceLocalName];
|
|
414
|
+
if (!importedRef || importedRef.source !== 'services')
|
|
415
|
+
return;
|
|
416
|
+
|
|
417
|
+
const routePath = [
|
|
418
|
+
importedRef.imported || serviceLocalName,
|
|
419
|
+
...segments.slice(2)
|
|
420
|
+
].join('/');
|
|
421
|
+
|
|
422
|
+
if (!this.routesIndex.all.has(routePath))
|
|
423
|
+
return;
|
|
424
|
+
|
|
425
|
+
// Ensure the parent function checks that `context` exists
|
|
426
|
+
if (!this.contextGuardedClassMethods.has(classMethodPath.node)) {
|
|
427
|
+
const guard = t.ifStatement(
|
|
428
|
+
t.binaryExpression(
|
|
429
|
+
'===',
|
|
430
|
+
t.unaryExpression('typeof', t.identifier('context')),
|
|
431
|
+
t.stringLiteral('undefined')
|
|
432
|
+
),
|
|
433
|
+
t.blockStatement([
|
|
434
|
+
t.throwStatement(
|
|
435
|
+
t.newExpression(t.identifier('Error'), [
|
|
436
|
+
t.stringLiteral('context variable should be passed in this function')
|
|
437
|
+
])
|
|
438
|
+
)
|
|
439
|
+
])
|
|
440
|
+
);
|
|
441
|
+
classMethodPath.get('body').unshiftContainer('body', guard);
|
|
442
|
+
this.contextGuardedClassMethods.add(classMethodPath.node);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Ensure call arguments: second argument is `context`
|
|
446
|
+
const args = path.node.arguments;
|
|
447
|
+
if (args.length === 0) {
|
|
448
|
+
args.push(t.identifier('undefined'), t.identifier('context'));
|
|
449
|
+
} else if (args.length === 1) {
|
|
450
|
+
args.push(t.identifier('context'));
|
|
451
|
+
} else {
|
|
452
|
+
args[1] = t.identifier('context') as any;
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
|
|
206
456
|
// This visitor fires for every class method.
|
|
207
457
|
ClassMethod(path) {
|
|
208
458
|
|