@adonisjs/assembler 8.0.0-next.3 → 8.0.0-next.31
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 +341 -53
- package/build/codemod_exception-vyN1VXuX.js +137 -0
- package/build/helpers-DDurYRsZ.js +72 -0
- package/build/index.d.ts +4 -0
- package/build/index.js +925 -1319
- package/build/main-BeV45LeF.js +246 -0
- package/build/main-CknPN3rJ.js +188 -0
- package/build/src/bundler.d.ts +46 -3
- package/build/src/code_scanners/routes_scanner/main.d.ts +65 -11
- package/build/src/code_scanners/routes_scanner/main.js +4 -0
- package/build/src/code_scanners/routes_scanner/validator_extractor.d.ts +12 -4
- package/build/src/code_transformer/main.d.ts +58 -44
- package/build/src/code_transformer/main.js +501 -599
- package/build/src/code_transformer/rc_file_transformer.d.ts +83 -5
- package/build/src/debug.d.ts +13 -1
- package/build/src/dev_server.d.ts +94 -17
- package/build/src/exceptions/codemod_exception.d.ts +178 -0
- package/build/src/file_buffer.d.ts +87 -0
- package/build/src/file_system.d.ts +46 -8
- package/build/src/helpers.d.ts +79 -4
- package/build/src/helpers.js +2 -0
- package/build/src/index_generator/main.d.ts +68 -0
- package/build/src/index_generator/main.js +3 -0
- package/build/src/index_generator/source.d.ts +60 -0
- package/build/src/paths_resolver.d.ts +29 -3
- package/build/src/shortcuts_manager.d.ts +42 -4
- package/build/src/test_runner.d.ts +59 -12
- package/build/src/types/code_scanners.d.ts +160 -30
- package/build/src/types/code_transformer.d.ts +188 -19
- package/build/src/types/common.d.ts +298 -55
- package/build/src/types/hooks.d.ts +238 -22
- package/build/src/types/main.d.ts +15 -1
- package/build/src/types/main.js +1 -0
- package/build/src/utils.d.ts +96 -15
- package/build/src/virtual_file_system.d.ts +112 -0
- package/build/virtual_file_system-bGeoWsK-.js +285 -0
- package/package.json +47 -37
- package/build/chunk-RR4HCA4M.js +0 -7
- package/build/src/ast_file_system.d.ts +0 -17
|
@@ -1,612 +1,514 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
} from "
|
|
1
|
+
import { t as CodemodException } from "../../codemod_exception-vyN1VXuX.js";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { detectPackageManager, installPackage } from "@antfu/install-pkg";
|
|
4
|
+
import { ImportsBag } from "@poppinss/utils";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { Node, Project, QuoteKind, SyntaxKind } from "ts-morph";
|
|
7
|
+
const ALLOWED_ENVIRONMENTS = [
|
|
8
|
+
"web",
|
|
9
|
+
"console",
|
|
10
|
+
"test",
|
|
11
|
+
"repl"
|
|
12
|
+
];
|
|
13
|
+
var RcFileTransformer = class {
|
|
14
|
+
#cwd;
|
|
15
|
+
#project;
|
|
16
|
+
#editorSettings = {
|
|
17
|
+
indentSize: 2,
|
|
18
|
+
convertTabsToSpaces: true,
|
|
19
|
+
trimTrailingWhitespace: true,
|
|
20
|
+
ensureNewLineAtEndOfFile: true,
|
|
21
|
+
indentStyle: 2,
|
|
22
|
+
semicolons: "remove"
|
|
23
|
+
};
|
|
24
|
+
constructor(cwd, project) {
|
|
25
|
+
this.#cwd = cwd;
|
|
26
|
+
this.#project = project;
|
|
27
|
+
}
|
|
28
|
+
#getRcFileOrThrow() {
|
|
29
|
+
const filePath = "adonisrc.ts";
|
|
30
|
+
const rcFileUrl = fileURLToPath(new URL(`./${filePath}`, this.#cwd));
|
|
31
|
+
const file = this.#project.getSourceFile(rcFileUrl);
|
|
32
|
+
if (!file) throw CodemodException.missingRcFile(filePath, `import { defineConfig } from '@adonisjs/core/app'
|
|
4
33
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
QuoteKind,
|
|
18
|
-
SyntaxKind as SyntaxKind2
|
|
19
|
-
} from "ts-morph";
|
|
34
|
+
export default defineConfig({
|
|
35
|
+
// Add your configuration here
|
|
36
|
+
})`);
|
|
37
|
+
return file;
|
|
38
|
+
}
|
|
39
|
+
#isInSpecificEnvironment(environments) {
|
|
40
|
+
if (!environments) return false;
|
|
41
|
+
return !!ALLOWED_ENVIRONMENTS.find((env) => !environments.includes(env));
|
|
42
|
+
}
|
|
43
|
+
#locateDefineConfigCallOrThrow(file) {
|
|
44
|
+
const call = file.getDescendantsOfKind(SyntaxKind.CallExpression).find((statement) => statement.getExpression().getText() === "defineConfig");
|
|
45
|
+
if (!call) throw CodemodException.invalidRcFile("adonisrc.ts", `import { defineConfig } from '@adonisjs/core/app'
|
|
20
46
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
return !!ALLOWED_ENVIRONMENTS.find((env) => !environments.includes(env));
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Locate the `defineConfig` call inside the `adonisrc.ts` file
|
|
65
|
-
*/
|
|
66
|
-
#locateDefineConfigCallOrThrow(file) {
|
|
67
|
-
const call = file.getDescendantsOfKind(SyntaxKind.CallExpression).find((statement) => statement.getExpression().getText() === "defineConfig");
|
|
68
|
-
if (!call) {
|
|
69
|
-
throw new Error("Could not locate the defineConfig call.");
|
|
70
|
-
}
|
|
71
|
-
return call;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Return the ObjectLiteralExpression of the defineConfig call
|
|
75
|
-
*/
|
|
76
|
-
#getDefineConfigObjectOrThrow(defineConfigCall) {
|
|
77
|
-
const configObject = defineConfigCall.getArguments()[0].asKindOrThrow(SyntaxKind.ObjectLiteralExpression);
|
|
78
|
-
return configObject;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Check if the defineConfig() call has the property assignment
|
|
82
|
-
* inside it or not. If not, it will create one and return it.
|
|
83
|
-
*/
|
|
84
|
-
#getPropertyAssignmentInDefineConfigCall(propertyName, initializer) {
|
|
85
|
-
const file = this.#getRcFileOrThrow();
|
|
86
|
-
const defineConfigCall = this.#locateDefineConfigCallOrThrow(file);
|
|
87
|
-
const configObject = this.#getDefineConfigObjectOrThrow(defineConfigCall);
|
|
88
|
-
let property = configObject.getProperty(propertyName);
|
|
89
|
-
if (!property) {
|
|
90
|
-
configObject.addPropertyAssignment({ name: propertyName, initializer });
|
|
91
|
-
property = configObject.getProperty(propertyName);
|
|
92
|
-
}
|
|
93
|
-
return property;
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Extract list of imported modules from an ArrayLiteralExpression
|
|
97
|
-
*
|
|
98
|
-
* It assumes that the array can have two types of elements:
|
|
99
|
-
*
|
|
100
|
-
* - Simple lazy imported modules: [() => import('path/to/file')]
|
|
101
|
-
* - Or an object entry: [{ file: () => import('path/to/file'), environment: ['web', 'console'] }]
|
|
102
|
-
* where the `file` property is a lazy imported module.
|
|
103
|
-
*/
|
|
104
|
-
#extractModulesFromArray(array) {
|
|
105
|
-
const modules = array.getElements().map((element) => {
|
|
106
|
-
if (Node.isArrowFunction(element)) {
|
|
107
|
-
const importExp = element.getFirstDescendantByKindOrThrow(SyntaxKind.CallExpression);
|
|
108
|
-
const literal = importExp.getFirstDescendantByKindOrThrow(SyntaxKind.StringLiteral);
|
|
109
|
-
return literal.getLiteralValue();
|
|
110
|
-
}
|
|
111
|
-
if (Node.isObjectLiteralExpression(element)) {
|
|
112
|
-
const fileProp = element.getPropertyOrThrow("file");
|
|
113
|
-
const arrowFn = fileProp.getFirstDescendantByKindOrThrow(SyntaxKind.ArrowFunction);
|
|
114
|
-
const importExp = arrowFn.getFirstDescendantByKindOrThrow(SyntaxKind.CallExpression);
|
|
115
|
-
const literal = importExp.getFirstDescendantByKindOrThrow(SyntaxKind.StringLiteral);
|
|
116
|
-
return literal.getLiteralValue();
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
return modules.filter(Boolean);
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Extract a specific property from an ArrayLiteralExpression
|
|
123
|
-
* that contains object entries.
|
|
124
|
-
*
|
|
125
|
-
* This function is mainly used for extractring the `pattern` property
|
|
126
|
-
* when adding a new meta files entry, or the `name` property when
|
|
127
|
-
* adding a new test suite.
|
|
128
|
-
*/
|
|
129
|
-
#extractPropertyFromArray(array, propertyName) {
|
|
130
|
-
const property = array.getElements().map((el) => {
|
|
131
|
-
if (!Node.isObjectLiteralExpression(el)) return;
|
|
132
|
-
const nameProp = el.getPropertyOrThrow(propertyName);
|
|
133
|
-
if (!Node.isPropertyAssignment(nameProp)) return;
|
|
134
|
-
const name = nameProp.getInitializerIfKindOrThrow(SyntaxKind.StringLiteral);
|
|
135
|
-
return name.getLiteralValue();
|
|
136
|
-
});
|
|
137
|
-
return property.filter(Boolean);
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Build a new module entry for the preloads and providers array
|
|
141
|
-
* based upon the environments specified
|
|
142
|
-
*/
|
|
143
|
-
#buildNewModuleEntry(modulePath, environments) {
|
|
144
|
-
if (!this.#isInSpecificEnvironment(environments)) {
|
|
145
|
-
return `() => import('${modulePath}')`;
|
|
146
|
-
}
|
|
147
|
-
return `{
|
|
47
|
+
export default defineConfig({
|
|
48
|
+
// Add your configuration here
|
|
49
|
+
})`, "Could not locate the defineConfig call.");
|
|
50
|
+
return call;
|
|
51
|
+
}
|
|
52
|
+
#getDefineConfigObjectOrThrow(defineConfigCall) {
|
|
53
|
+
return defineConfigCall.getArguments()[0].asKindOrThrow(SyntaxKind.ObjectLiteralExpression);
|
|
54
|
+
}
|
|
55
|
+
#getPropertyAssignmentInDefineConfigCall(propertyName, initializer) {
|
|
56
|
+
const file = this.#getRcFileOrThrow();
|
|
57
|
+
const defineConfigCall = this.#locateDefineConfigCallOrThrow(file);
|
|
58
|
+
const configObject = this.#getDefineConfigObjectOrThrow(defineConfigCall);
|
|
59
|
+
let property = configObject.getProperty(propertyName);
|
|
60
|
+
if (!property) {
|
|
61
|
+
configObject.addPropertyAssignment({
|
|
62
|
+
name: propertyName,
|
|
63
|
+
initializer
|
|
64
|
+
});
|
|
65
|
+
property = configObject.getProperty(propertyName);
|
|
66
|
+
}
|
|
67
|
+
return property;
|
|
68
|
+
}
|
|
69
|
+
#extractModulesFromArray(array) {
|
|
70
|
+
return array.getElements().map((element) => {
|
|
71
|
+
if (Node.isArrowFunction(element)) return element.getFirstDescendantByKindOrThrow(SyntaxKind.CallExpression).getFirstDescendantByKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue();
|
|
72
|
+
if (Node.isObjectLiteralExpression(element)) return element.getPropertyOrThrow("file").getFirstDescendantByKindOrThrow(SyntaxKind.ArrowFunction).getFirstDescendantByKindOrThrow(SyntaxKind.CallExpression).getFirstDescendantByKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue();
|
|
73
|
+
}).filter(Boolean);
|
|
74
|
+
}
|
|
75
|
+
#extractPropertyFromArray(array, propertyName) {
|
|
76
|
+
return array.getElements().map((el) => {
|
|
77
|
+
if (!Node.isObjectLiteralExpression(el)) return;
|
|
78
|
+
const nameProp = el.getPropertyOrThrow(propertyName);
|
|
79
|
+
if (!Node.isPropertyAssignment(nameProp)) return;
|
|
80
|
+
return nameProp.getInitializerIfKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue();
|
|
81
|
+
}).filter(Boolean);
|
|
82
|
+
}
|
|
83
|
+
#buildNewModuleEntry(modulePath, environments) {
|
|
84
|
+
if (!this.#isInSpecificEnvironment(environments)) return `() => import('${modulePath}')`;
|
|
85
|
+
return `{
|
|
148
86
|
file: () => import('${modulePath}'),
|
|
149
87
|
environment: [${environments?.map((env) => `'${env}'`).join(", ")}],
|
|
150
88
|
}`;
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const existingPreloadedFiles = this.#extractModulesFromArray(preloadsArray);
|
|
176
|
-
const isDuplicate = existingPreloadedFiles.includes(modulePath);
|
|
177
|
-
if (isDuplicate) {
|
|
178
|
-
return this;
|
|
179
|
-
}
|
|
180
|
-
preloadsArray.addElement(this.#buildNewModuleEntry(modulePath, environments));
|
|
181
|
-
return this;
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Add a new provider to the rcFile
|
|
185
|
-
*/
|
|
186
|
-
addProvider(providerPath, environments) {
|
|
187
|
-
const property = this.#getPropertyAssignmentInDefineConfigCall("providers", "[]");
|
|
188
|
-
const providersArray = property.getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression);
|
|
189
|
-
const existingProviderPaths = this.#extractModulesFromArray(providersArray);
|
|
190
|
-
const isDuplicate = existingProviderPaths.includes(providerPath);
|
|
191
|
-
if (isDuplicate) {
|
|
192
|
-
return this;
|
|
193
|
-
}
|
|
194
|
-
providersArray.addElement(this.#buildNewModuleEntry(providerPath, environments));
|
|
195
|
-
return this;
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Add a new meta file to the rcFile
|
|
199
|
-
*/
|
|
200
|
-
addMetaFile(globPattern, reloadServer = false) {
|
|
201
|
-
const property = this.#getPropertyAssignmentInDefineConfigCall("metaFiles", "[]");
|
|
202
|
-
const metaFilesArray = property.getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression);
|
|
203
|
-
const alreadyDefinedPatterns = this.#extractPropertyFromArray(metaFilesArray, "pattern");
|
|
204
|
-
if (alreadyDefinedPatterns.includes(globPattern)) {
|
|
205
|
-
return this;
|
|
206
|
-
}
|
|
207
|
-
metaFilesArray.addElement(
|
|
208
|
-
`{
|
|
89
|
+
}
|
|
90
|
+
addCommand(commandPath) {
|
|
91
|
+
const commandsArray = this.#getPropertyAssignmentInDefineConfigCall("commands", "[]").getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression);
|
|
92
|
+
const commandString = `() => import('${commandPath}')`;
|
|
93
|
+
if (commandsArray.getElements().some((el) => el.getText() === commandString)) return this;
|
|
94
|
+
commandsArray.addElement(commandString);
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
addPreloadFile(modulePath, environments) {
|
|
98
|
+
const preloadsArray = this.#getPropertyAssignmentInDefineConfigCall("preloads", "[]").getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression);
|
|
99
|
+
if (this.#extractModulesFromArray(preloadsArray).includes(modulePath)) return this;
|
|
100
|
+
preloadsArray.addElement(this.#buildNewModuleEntry(modulePath, environments));
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
addProvider(providerPath, environments) {
|
|
104
|
+
const providersArray = this.#getPropertyAssignmentInDefineConfigCall("providers", "[]").getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression);
|
|
105
|
+
if (this.#extractModulesFromArray(providersArray).includes(providerPath)) return this;
|
|
106
|
+
providersArray.addElement(this.#buildNewModuleEntry(providerPath, environments));
|
|
107
|
+
return this;
|
|
108
|
+
}
|
|
109
|
+
addMetaFile(globPattern, reloadServer = false) {
|
|
110
|
+
const metaFilesArray = this.#getPropertyAssignmentInDefineConfigCall("metaFiles", "[]").getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression);
|
|
111
|
+
if (this.#extractPropertyFromArray(metaFilesArray, "pattern").includes(globPattern)) return this;
|
|
112
|
+
metaFilesArray.addElement(`{
|
|
209
113
|
pattern: '${globPattern}',
|
|
210
114
|
reloadServer: ${reloadServer},
|
|
211
|
-
}`
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Add a new test suite to the rcFile
|
|
235
|
-
*/
|
|
236
|
-
addSuite(suiteName, files, timeout) {
|
|
237
|
-
const testProperty = this.#getPropertyAssignmentInDefineConfigCall(
|
|
238
|
-
"tests",
|
|
239
|
-
`{ suites: [], forceExit: true, timeout: 2000 }`
|
|
240
|
-
);
|
|
241
|
-
const property = testProperty.getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression).getPropertyOrThrow("suites");
|
|
242
|
-
const suitesArray = property.getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression);
|
|
243
|
-
const existingSuitesNames = this.#extractPropertyFromArray(suitesArray, "name");
|
|
244
|
-
if (existingSuitesNames.includes(suiteName)) {
|
|
245
|
-
return this;
|
|
246
|
-
}
|
|
247
|
-
const filesArray = Array.isArray(files) ? files : [files];
|
|
248
|
-
suitesArray.addElement(
|
|
249
|
-
`{
|
|
115
|
+
}`);
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
setDirectory(key, value) {
|
|
119
|
+
this.#getPropertyAssignmentInDefineConfigCall("directories", "{}").getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression).addPropertyAssignment({
|
|
120
|
+
name: key,
|
|
121
|
+
initializer: `'${value}'`
|
|
122
|
+
});
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
setCommandAlias(alias, command) {
|
|
126
|
+
this.#getPropertyAssignmentInDefineConfigCall("commandsAliases", "{}").getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression).addPropertyAssignment({
|
|
127
|
+
name: alias,
|
|
128
|
+
initializer: `'${command}'`
|
|
129
|
+
});
|
|
130
|
+
return this;
|
|
131
|
+
}
|
|
132
|
+
addSuite(suiteName, files, timeout) {
|
|
133
|
+
const suitesArray = this.#getPropertyAssignmentInDefineConfigCall("tests", `{ suites: [], forceExit: true, timeout: 2000 }`).getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression).getPropertyOrThrow("suites").getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression);
|
|
134
|
+
if (this.#extractPropertyFromArray(suitesArray, "name").includes(suiteName)) return this;
|
|
135
|
+
const filesArray = Array.isArray(files) ? files : [files];
|
|
136
|
+
suitesArray.addElement(`{
|
|
250
137
|
name: '${suiteName}',
|
|
251
138
|
files: [${filesArray.map((file) => `'${file}'`).join(", ")}],
|
|
252
139
|
timeout: ${timeout ?? 2e3},
|
|
253
|
-
}`
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
140
|
+
}`);
|
|
141
|
+
return this;
|
|
142
|
+
}
|
|
143
|
+
addAssemblerHook(type, value, raw = false) {
|
|
144
|
+
const hooks = this.#getPropertyAssignmentInDefineConfigCall("hooks", "{}").getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression);
|
|
145
|
+
let hookArray = hooks.getProperty(type);
|
|
146
|
+
if (!hookArray) {
|
|
147
|
+
hooks.addPropertyAssignment({
|
|
148
|
+
name: type,
|
|
149
|
+
initializer: "[]"
|
|
150
|
+
});
|
|
151
|
+
hookArray = hooks.getProperty(type);
|
|
152
|
+
}
|
|
153
|
+
const hooksArray = hookArray.getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression);
|
|
154
|
+
if (raw) hooksArray.addElement(value);
|
|
155
|
+
else {
|
|
156
|
+
if (this.#extractModulesFromArray(hooksArray).includes(value)) return this;
|
|
157
|
+
hooksArray.addElement(`() => import('${value}')`);
|
|
158
|
+
}
|
|
159
|
+
return this;
|
|
160
|
+
}
|
|
161
|
+
addNamedImport(specifier, names) {
|
|
162
|
+
this.#getRcFileOrThrow().addImportDeclaration({
|
|
163
|
+
moduleSpecifier: specifier,
|
|
164
|
+
namedImports: names
|
|
165
|
+
});
|
|
166
|
+
return this;
|
|
167
|
+
}
|
|
168
|
+
addDefaultImport(specifier, name) {
|
|
169
|
+
this.#getRcFileOrThrow().addImportDeclaration({
|
|
170
|
+
moduleSpecifier: specifier,
|
|
171
|
+
defaultImport: name
|
|
172
|
+
});
|
|
173
|
+
return this;
|
|
174
|
+
}
|
|
175
|
+
getDirectory(key, defaultValue) {
|
|
176
|
+
try {
|
|
177
|
+
const file = this.#getRcFileOrThrow();
|
|
178
|
+
const defineConfigCall = this.#locateDefineConfigCallOrThrow(file);
|
|
179
|
+
const directoriesProperty = this.#getDefineConfigObjectOrThrow(defineConfigCall).getProperty("directories");
|
|
180
|
+
if (!directoriesProperty || !Node.isPropertyAssignment(directoriesProperty)) return defaultValue;
|
|
181
|
+
const directoriesObject = directoriesProperty.getInitializer();
|
|
182
|
+
if (!directoriesObject || !Node.isObjectLiteralExpression(directoriesObject)) return defaultValue;
|
|
183
|
+
const property = directoriesObject.getProperty(key);
|
|
184
|
+
if (!property || !Node.isPropertyAssignment(property)) return defaultValue;
|
|
185
|
+
const initializer = property.getInitializer();
|
|
186
|
+
if (!initializer || !Node.isStringLiteral(initializer)) return defaultValue;
|
|
187
|
+
return initializer.getLiteralValue();
|
|
188
|
+
} catch {
|
|
189
|
+
return defaultValue;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
save() {
|
|
193
|
+
const file = this.#getRcFileOrThrow();
|
|
194
|
+
file.formatText(this.#editorSettings);
|
|
195
|
+
return file.save();
|
|
196
|
+
}
|
|
284
197
|
};
|
|
285
|
-
|
|
286
|
-
// src/code_transformer/main.ts
|
|
287
198
|
var CodeTransformer = class {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
},
|
|
603
|
-
[`export const ${exportName} = {`]
|
|
604
|
-
).concat("}").join("\n");
|
|
605
|
-
});
|
|
606
|
-
await mkdir(outputDir, { recursive: true });
|
|
607
|
-
await writeFile(outputPath, computeOutput(entries.flat(2)));
|
|
608
|
-
}
|
|
609
|
-
};
|
|
610
|
-
export {
|
|
611
|
-
CodeTransformer
|
|
199
|
+
installPackage = installPackage;
|
|
200
|
+
detectPackageManager = detectPackageManager;
|
|
201
|
+
#cwd;
|
|
202
|
+
#cwdPath;
|
|
203
|
+
project;
|
|
204
|
+
#editorSettings = {
|
|
205
|
+
indentSize: 2,
|
|
206
|
+
convertTabsToSpaces: true,
|
|
207
|
+
trimTrailingWhitespace: true,
|
|
208
|
+
ensureNewLineAtEndOfFile: true,
|
|
209
|
+
indentStyle: 2,
|
|
210
|
+
semicolons: "remove"
|
|
211
|
+
};
|
|
212
|
+
constructor(cwd) {
|
|
213
|
+
this.#cwd = cwd;
|
|
214
|
+
this.#cwdPath = fileURLToPath(this.#cwd);
|
|
215
|
+
this.project = new Project({
|
|
216
|
+
tsConfigFilePath: join(fileURLToPath(this.#cwd), "tsconfig.json"),
|
|
217
|
+
manipulationSettings: { quoteKind: QuoteKind.Single }
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
getDirectories() {
|
|
221
|
+
const rcFileTransformer = new RcFileTransformer(this.#cwd, this.project);
|
|
222
|
+
return {
|
|
223
|
+
start: rcFileTransformer.getDirectory("start", "start"),
|
|
224
|
+
tests: rcFileTransformer.getDirectory("tests", "tests"),
|
|
225
|
+
policies: rcFileTransformer.getDirectory("policies", "app/policies"),
|
|
226
|
+
validators: rcFileTransformer.getDirectory("validators", "app/validators"),
|
|
227
|
+
models: rcFileTransformer.getDirectory("models", "app/models"),
|
|
228
|
+
controllers: rcFileTransformer.getDirectory("controllers", "app/controllers")
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
#addToMiddlewareArray(file, target, middlewareEntry) {
|
|
232
|
+
const callExpressions = file.getDescendantsOfKind(SyntaxKind.CallExpression).filter((statement) => statement.getExpression().getText() === target);
|
|
233
|
+
if (!callExpressions.length) throw new Error(`Cannot find ${target} statement in the file.`);
|
|
234
|
+
const arrayLiteralExpression = callExpressions[0].getArguments()[0];
|
|
235
|
+
if (!arrayLiteralExpression || !Node.isArrayLiteralExpression(arrayLiteralExpression)) throw new Error(`Cannot find middleware array in ${target} statement.`);
|
|
236
|
+
const middleware = `() => import('${middlewareEntry.path}')`;
|
|
237
|
+
if (arrayLiteralExpression.getElements().findIndex((element) => element.getText() === middleware) === -1) if (middlewareEntry.position === "before") arrayLiteralExpression.insertElement(0, middleware);
|
|
238
|
+
else arrayLiteralExpression.addElement(middleware);
|
|
239
|
+
}
|
|
240
|
+
#addToNamedMiddleware(file, middlewareEntry) {
|
|
241
|
+
if (!middlewareEntry.name) throw new Error("Named middleware requires a name.");
|
|
242
|
+
const callArguments = file.getVariableDeclarationOrThrow("middleware").getInitializerIfKindOrThrow(SyntaxKind.CallExpression).getArguments();
|
|
243
|
+
if (callArguments.length === 0) throw new Error("Named middleware call has no arguments.");
|
|
244
|
+
const namedMiddlewareObject = callArguments[0];
|
|
245
|
+
if (!Node.isObjectLiteralExpression(namedMiddlewareObject)) throw new Error("The argument of the named middleware call is not an object literal.");
|
|
246
|
+
if (!namedMiddlewareObject.getProperty(middlewareEntry.name)) {
|
|
247
|
+
const middleware = `${middlewareEntry.name}: () => import('${middlewareEntry.path}')`;
|
|
248
|
+
namedMiddlewareObject.insertProperty(0, middleware);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
#addToPoliciesList(file, policyEntry) {
|
|
252
|
+
const policiesObject = file.getVariableDeclarationOrThrow("policies").getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression);
|
|
253
|
+
if (!policiesObject.getProperty(policyEntry.name)) {
|
|
254
|
+
const policy = `${policyEntry.name}: () => import('${policyEntry.path}')`;
|
|
255
|
+
policiesObject.insertProperty(0, policy);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
#addImportDeclarations(file, importDeclarations) {
|
|
259
|
+
importDeclarations.forEach((importDeclaration) => {
|
|
260
|
+
const existingImport = file.getImportDeclarations().find((mod) => mod.getModuleSpecifierValue() === importDeclaration.module);
|
|
261
|
+
if (existingImport && importDeclaration.isNamed) {
|
|
262
|
+
if (!existingImport.getNamedImports().find((namedImport) => namedImport.getName() === importDeclaration.identifier)) existingImport.addNamedImport(importDeclaration.identifier);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (existingImport) return;
|
|
266
|
+
file.addImportDeclaration({
|
|
267
|
+
...importDeclaration.isNamed ? { namedImports: [importDeclaration.identifier] } : { defaultImport: importDeclaration.identifier },
|
|
268
|
+
moduleSpecifier: importDeclaration.module
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
#addImportsFromImportInfo(file, imports) {
|
|
273
|
+
const importsBag = new ImportsBag();
|
|
274
|
+
for (const importInfo of imports) importsBag.add(importInfo);
|
|
275
|
+
const importDeclarations = importsBag.toArray().flatMap((moduleImport) => {
|
|
276
|
+
return (moduleImport.namedImports ?? []).map((symbol) => {
|
|
277
|
+
return {
|
|
278
|
+
isNamed: true,
|
|
279
|
+
module: moduleImport.source,
|
|
280
|
+
identifier: symbol
|
|
281
|
+
};
|
|
282
|
+
}).concat(moduleImport.defaultImport ? [{
|
|
283
|
+
isNamed: false,
|
|
284
|
+
module: moduleImport.source,
|
|
285
|
+
identifier: moduleImport.defaultImport
|
|
286
|
+
}] : []);
|
|
287
|
+
});
|
|
288
|
+
this.#addImportDeclarations(file, importDeclarations);
|
|
289
|
+
}
|
|
290
|
+
#addLeadingComment(writer, comment) {
|
|
291
|
+
if (!comment) return writer.blankLine();
|
|
292
|
+
return writer.blankLine().writeLine("/*").writeLine(`|----------------------------------------------------------`).writeLine(`| ${comment}`).writeLine(`|----------------------------------------------------------`).writeLine(`*/`);
|
|
293
|
+
}
|
|
294
|
+
async defineEnvValidations(definition) {
|
|
295
|
+
const filePath = `${this.getDirectories().start}/env.ts`;
|
|
296
|
+
const envUrl = join(this.#cwdPath, `./${filePath}`);
|
|
297
|
+
const file = this.project.getSourceFile(envUrl);
|
|
298
|
+
if (!file) throw CodemodException.missingEnvFile(filePath, definition);
|
|
299
|
+
const callExpressions = file.getDescendantsOfKind(SyntaxKind.CallExpression).filter((statement) => statement.getExpression().getText() === "Env.create");
|
|
300
|
+
if (!callExpressions.length) throw CodemodException.missingEnvCreate(filePath, definition);
|
|
301
|
+
const objectLiteralExpression = callExpressions[0].getArguments()[1];
|
|
302
|
+
if (!Node.isObjectLiteralExpression(objectLiteralExpression)) throw CodemodException.invalidEnvCreate(filePath, definition);
|
|
303
|
+
let shouldAddComment = true;
|
|
304
|
+
for (const [variable, validation] of Object.entries(definition.variables)) {
|
|
305
|
+
const existingProperty = objectLiteralExpression.getProperty(variable);
|
|
306
|
+
if (existingProperty) shouldAddComment = false;
|
|
307
|
+
if (!existingProperty) objectLiteralExpression.addPropertyAssignment({
|
|
308
|
+
name: variable,
|
|
309
|
+
initializer: validation,
|
|
310
|
+
leadingTrivia: (writer) => {
|
|
311
|
+
if (!shouldAddComment) return;
|
|
312
|
+
shouldAddComment = false;
|
|
313
|
+
return this.#addLeadingComment(writer, definition.leadingComment);
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
file.formatText(this.#editorSettings);
|
|
318
|
+
await file.save();
|
|
319
|
+
}
|
|
320
|
+
async addMiddlewareToStack(stack, middleware) {
|
|
321
|
+
const filePath = `${this.getDirectories().start}/kernel.ts`;
|
|
322
|
+
const kernelUrl = join(this.#cwdPath, `./${filePath}`);
|
|
323
|
+
const file = this.project.getSourceFile(kernelUrl);
|
|
324
|
+
if (!file) throw CodemodException.missingKernelFile(filePath, stack, middleware);
|
|
325
|
+
try {
|
|
326
|
+
for (const middlewareEntry of middleware) if (stack === "named") this.#addToNamedMiddleware(file, middlewareEntry);
|
|
327
|
+
else this.#addToMiddlewareArray(file, `${stack}.use`, middlewareEntry);
|
|
328
|
+
} catch (error) {
|
|
329
|
+
if (error instanceof Error) throw CodemodException.invalidMiddlewareStack(filePath, stack, middleware, error.message);
|
|
330
|
+
throw error;
|
|
331
|
+
}
|
|
332
|
+
file.formatText(this.#editorSettings);
|
|
333
|
+
await file.save();
|
|
334
|
+
}
|
|
335
|
+
async updateRcFile(callback) {
|
|
336
|
+
const rcFileTransformer = new RcFileTransformer(this.#cwd, this.project);
|
|
337
|
+
callback(rcFileTransformer);
|
|
338
|
+
await rcFileTransformer.save();
|
|
339
|
+
}
|
|
340
|
+
async addJapaPlugin(pluginCall, importDeclarations) {
|
|
341
|
+
const filePath = `${this.getDirectories().tests}/bootstrap.ts`;
|
|
342
|
+
const testBootstrapUrl = join(this.#cwdPath, `./${filePath}`);
|
|
343
|
+
const file = this.project.getSourceFile(testBootstrapUrl);
|
|
344
|
+
if (!file) throw CodemodException.missingJapaBootstrap(filePath, pluginCall, importDeclarations);
|
|
345
|
+
this.#addImportDeclarations(file, importDeclarations);
|
|
346
|
+
const pluginsArray = file.getVariableDeclaration("plugins")?.getInitializerIfKind(SyntaxKind.ArrayLiteralExpression);
|
|
347
|
+
if (pluginsArray) {
|
|
348
|
+
if (!pluginsArray.getElements().find((element) => element.getText() === pluginCall)) pluginsArray.addElement(pluginCall);
|
|
349
|
+
}
|
|
350
|
+
file.formatText(this.#editorSettings);
|
|
351
|
+
await file.save();
|
|
352
|
+
}
|
|
353
|
+
async addVitePlugin(pluginCall, importDeclarations) {
|
|
354
|
+
const filePath = "vite.config.ts";
|
|
355
|
+
const viteConfigTsUrl = join(this.#cwdPath, `./${filePath}`);
|
|
356
|
+
const file = this.project.getSourceFile(viteConfigTsUrl);
|
|
357
|
+
if (!file) throw CodemodException.missingViteConfig(filePath, pluginCall, importDeclarations);
|
|
358
|
+
try {
|
|
359
|
+
this.#addImportDeclarations(file, importDeclarations);
|
|
360
|
+
const defaultExport = file.getDefaultExportSymbol();
|
|
361
|
+
if (!defaultExport) throw new Error("Cannot find the default export in vite.config.ts");
|
|
362
|
+
const declaration = defaultExport.getDeclarations()[0];
|
|
363
|
+
const pluginsArray = (declaration.getChildrenOfKind(SyntaxKind.ObjectLiteralExpression)[0] || declaration.getChildrenOfKind(SyntaxKind.CallExpression)[0].getArguments()[0]).getPropertyOrThrow("plugins").getFirstChildByKindOrThrow(SyntaxKind.ArrayLiteralExpression);
|
|
364
|
+
if (!pluginsArray.getElements().find((element) => element.getText() === pluginCall)) pluginsArray.addElement(pluginCall);
|
|
365
|
+
} catch (error) {
|
|
366
|
+
if (error instanceof CodemodException) throw error;
|
|
367
|
+
if (error instanceof Error) throw CodemodException.invalidViteConfig(filePath, pluginCall, importDeclarations, error.message);
|
|
368
|
+
throw error;
|
|
369
|
+
}
|
|
370
|
+
file.formatText(this.#editorSettings);
|
|
371
|
+
await file.save();
|
|
372
|
+
}
|
|
373
|
+
async addPolicies(policies) {
|
|
374
|
+
const filePath = `${this.getDirectories().policies}/main.ts`;
|
|
375
|
+
const policiesUrl = join(this.#cwdPath, `./${filePath}`);
|
|
376
|
+
const file = this.project.getSourceFile(policiesUrl);
|
|
377
|
+
if (!file) throw CodemodException.missingPoliciesFile(filePath, policies);
|
|
378
|
+
try {
|
|
379
|
+
for (const policy of policies) this.#addToPoliciesList(file, policy);
|
|
380
|
+
} catch (error) {
|
|
381
|
+
if (error instanceof Error) throw CodemodException.invalidPoliciesFile(filePath, policies, error.message);
|
|
382
|
+
throw error;
|
|
383
|
+
}
|
|
384
|
+
file.formatText(this.#editorSettings);
|
|
385
|
+
await file.save();
|
|
386
|
+
}
|
|
387
|
+
async addValidator(definition) {
|
|
388
|
+
const filePath = `${this.getDirectories().validators}/${definition.validatorFileName}`;
|
|
389
|
+
const validatorFileUrl = join(this.#cwdPath, `./${filePath}`);
|
|
390
|
+
let file = this.project.getSourceFile(validatorFileUrl);
|
|
391
|
+
if (!file) try {
|
|
392
|
+
file = this.project.addSourceFileAtPath(validatorFileUrl);
|
|
393
|
+
} catch {}
|
|
394
|
+
if (!file) {
|
|
395
|
+
file = this.project.createSourceFile(validatorFileUrl, definition.contents);
|
|
396
|
+
file.formatText(this.#editorSettings);
|
|
397
|
+
await file.save();
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (file.getVariableDeclaration(definition.exportName)) return;
|
|
401
|
+
file.addStatements(`\n${definition.contents}`);
|
|
402
|
+
file.formatText(this.#editorSettings);
|
|
403
|
+
await file.save();
|
|
404
|
+
}
|
|
405
|
+
async addLimiter(definition) {
|
|
406
|
+
const filePath = `${this.getDirectories().start}/${definition.limiterFileName}`;
|
|
407
|
+
const limiterFileUrl = join(this.#cwdPath, `./${filePath}`);
|
|
408
|
+
let file = this.project.getSourceFile(limiterFileUrl);
|
|
409
|
+
if (!file) try {
|
|
410
|
+
file = this.project.addSourceFileAtPath(limiterFileUrl);
|
|
411
|
+
} catch {}
|
|
412
|
+
if (!file) {
|
|
413
|
+
file = this.project.createSourceFile(limiterFileUrl, definition.contents);
|
|
414
|
+
file.formatText(this.#editorSettings);
|
|
415
|
+
await file.save();
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
if (file.getVariableDeclaration(definition.exportName)) return;
|
|
419
|
+
file.addStatements(`\n${definition.contents}`);
|
|
420
|
+
file.formatText(this.#editorSettings);
|
|
421
|
+
await file.save();
|
|
422
|
+
}
|
|
423
|
+
async addModelMixins(modelFileName, mixins) {
|
|
424
|
+
const filePath = `${this.getDirectories().models}/${modelFileName}`;
|
|
425
|
+
const modelFileUrl = join(this.#cwdPath, `./${filePath}`);
|
|
426
|
+
let file = this.project.getSourceFile(modelFileUrl);
|
|
427
|
+
if (!file) try {
|
|
428
|
+
file = this.project.addSourceFileAtPath(modelFileUrl);
|
|
429
|
+
} catch {
|
|
430
|
+
throw new Error(`Could not find source file at path: "${filePath}"`);
|
|
431
|
+
}
|
|
432
|
+
const defaultExportSymbol = file.getDefaultExportSymbol();
|
|
433
|
+
if (!defaultExportSymbol) throw new Error(`Could not find default export in "${filePath}". The model must have a default export class.`);
|
|
434
|
+
const declarations = defaultExportSymbol.getDeclarations();
|
|
435
|
+
if (declarations.length === 0) throw new Error(`Could not find default export declaration in "${filePath}".`);
|
|
436
|
+
const declaration = declarations[0];
|
|
437
|
+
if (!Node.isClassDeclaration(declaration)) throw new Error(`Default export in "${filePath}" is not a class. The model must be exported as a class.`);
|
|
438
|
+
const mixinImports = mixins.map((mixin) => {
|
|
439
|
+
if (mixin.importType === "named") return {
|
|
440
|
+
source: mixin.importPath,
|
|
441
|
+
namedImports: [mixin.name]
|
|
442
|
+
};
|
|
443
|
+
else return {
|
|
444
|
+
source: mixin.importPath,
|
|
445
|
+
defaultImport: mixin.name
|
|
446
|
+
};
|
|
447
|
+
});
|
|
448
|
+
this.#addImportsFromImportInfo(file, mixinImports);
|
|
449
|
+
const heritageClause = declaration.getHeritageClauseByKind(SyntaxKind.ExtendsKeyword);
|
|
450
|
+
if (!heritageClause) throw new Error(`Could not find extends clause in "${filePath}".`);
|
|
451
|
+
const extendsExpression = heritageClause.getTypeNodes()[0];
|
|
452
|
+
if (!extendsExpression) throw new Error(`Could not find extends expression in "${filePath}".`);
|
|
453
|
+
const extendsExpressionNode = extendsExpression.getExpression();
|
|
454
|
+
let composeCall;
|
|
455
|
+
if (Node.isCallExpression(extendsExpressionNode)) {
|
|
456
|
+
if (extendsExpressionNode.getExpression().getText() === "compose") composeCall = extendsExpressionNode;
|
|
457
|
+
}
|
|
458
|
+
const mixinCalls = mixins.map((mixin) => {
|
|
459
|
+
const args = mixin.args && mixin.args.length > 0 ? mixin.args.join(", ") : "";
|
|
460
|
+
return `${mixin.name}(${args})`;
|
|
461
|
+
});
|
|
462
|
+
if (composeCall && Node.isCallExpression(composeCall)) {
|
|
463
|
+
const existingArgsText = composeCall.getArguments().map((arg) => arg.getText());
|
|
464
|
+
const newMixinCalls = mixinCalls.filter((mixinCall) => {
|
|
465
|
+
const mixinFunctionName = mixinCall.split("(")[0];
|
|
466
|
+
return !existingArgsText.some((existingArg) => {
|
|
467
|
+
return existingArg.includes(`${mixinFunctionName}(`);
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
const newArgs = [...existingArgsText, ...newMixinCalls];
|
|
471
|
+
composeCall.replaceWithText(`compose(${newArgs.join(", ")})`);
|
|
472
|
+
} else {
|
|
473
|
+
this.#addImportDeclarations(file, [{
|
|
474
|
+
isNamed: true,
|
|
475
|
+
module: "@adonisjs/core/helpers",
|
|
476
|
+
identifier: "compose"
|
|
477
|
+
}]);
|
|
478
|
+
const newExtends = `compose(${extendsExpressionNode.getText()}, ${mixinCalls.join(", ")})`;
|
|
479
|
+
extendsExpression.replaceWithText(newExtends);
|
|
480
|
+
}
|
|
481
|
+
file.formatText(this.#editorSettings);
|
|
482
|
+
await file.save();
|
|
483
|
+
}
|
|
484
|
+
async addControllerMethod(definition) {
|
|
485
|
+
const filePath = `${this.getDirectories().controllers}/${definition.controllerFileName}`;
|
|
486
|
+
const controllerFileUrl = join(this.#cwdPath, `./${filePath}`);
|
|
487
|
+
let file = this.project.getSourceFile(controllerFileUrl);
|
|
488
|
+
if (!file) try {
|
|
489
|
+
file = this.project.addSourceFileAtPath(controllerFileUrl);
|
|
490
|
+
} catch {}
|
|
491
|
+
if (!file) {
|
|
492
|
+
const contents = `export default class ${definition.className} {
|
|
493
|
+
${definition.contents}
|
|
494
|
+
}`;
|
|
495
|
+
file = this.project.createSourceFile(controllerFileUrl, contents);
|
|
496
|
+
if (definition.imports) this.#addImportsFromImportInfo(file, definition.imports);
|
|
497
|
+
file.formatText(this.#editorSettings);
|
|
498
|
+
await file.save();
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
const defaultExportSymbol = file.getDefaultExportSymbol();
|
|
502
|
+
if (!defaultExportSymbol) throw new Error(`Could not find default export in "${filePath}". The controller must have a default export class.`);
|
|
503
|
+
const declarations = defaultExportSymbol.getDeclarations();
|
|
504
|
+
if (declarations.length === 0) throw new Error(`Could not find default export declaration in "${filePath}".`);
|
|
505
|
+
const declaration = declarations[0];
|
|
506
|
+
if (!Node.isClassDeclaration(declaration)) throw new Error(`Default export in "${filePath}" is not a class. The controller must be exported as a class.`);
|
|
507
|
+
if (declaration.getMethod(definition.name)) return;
|
|
508
|
+
if (definition.imports) this.#addImportsFromImportInfo(file, definition.imports);
|
|
509
|
+
declaration.addMember(definition.contents);
|
|
510
|
+
file.formatText(this.#editorSettings);
|
|
511
|
+
await file.save();
|
|
512
|
+
}
|
|
612
513
|
};
|
|
514
|
+
export { CodeTransformer };
|