@adonisjs/assembler 8.0.0-next.3 → 8.0.0-next.30

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