@adonisjs/assembler 6.1.3-9 → 7.0.0-1

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.
@@ -0,0 +1,478 @@
1
+ // src/code_transformer/main.ts
2
+ import { join } from "node:path";
3
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
4
+ import { installPackage, detectPackageManager } from "@antfu/install-pkg";
5
+ import {
6
+ Node as Node2,
7
+ Project as Project2,
8
+ QuoteKind,
9
+ SyntaxKind as SyntaxKind2
10
+ } from "ts-morph";
11
+
12
+ // src/code_transformer/rc_file_transformer.ts
13
+ import { fileURLToPath } from "node:url";
14
+ import {
15
+ Node,
16
+ SyntaxKind
17
+ } from "ts-morph";
18
+ var RcFileTransformer = class {
19
+ #cwd;
20
+ #project;
21
+ /**
22
+ * Settings to use when persisting files
23
+ */
24
+ #editorSettings = {
25
+ indentSize: 2,
26
+ convertTabsToSpaces: true,
27
+ trimTrailingWhitespace: true,
28
+ ensureNewLineAtEndOfFile: true,
29
+ indentStyle: 2,
30
+ // @ts-expect-error SemicolonPreference doesn't seem to be re-exported from ts-morph
31
+ semicolons: "remove"
32
+ };
33
+ constructor(cwd, project) {
34
+ this.#cwd = cwd;
35
+ this.#project = project;
36
+ }
37
+ /**
38
+ * Get the `adonisrc.ts` source file
39
+ */
40
+ #getRcFileOrThrow() {
41
+ const kernelUrl = fileURLToPath(new URL("./adonisrc.ts", this.#cwd));
42
+ return this.#project.getSourceFileOrThrow(kernelUrl);
43
+ }
44
+ /**
45
+ * Check if environments array has a subset of available environments
46
+ */
47
+ #isInSpecificEnvironment(environments) {
48
+ if (!environments) {
49
+ return false;
50
+ }
51
+ return !!["web", "console", "test", "repl"].find(
52
+ (env) => !environments.includes(env)
53
+ );
54
+ }
55
+ /**
56
+ * Locate the `defineConfig` call inside the `adonisrc.ts` file
57
+ */
58
+ #locateDefineConfigCallOrThrow(file) {
59
+ const call = file.getDescendantsOfKind(SyntaxKind.CallExpression).find((statement) => statement.getExpression().getText() === "defineConfig");
60
+ if (!call) {
61
+ throw new Error("Could not locate the defineConfig call.");
62
+ }
63
+ return call;
64
+ }
65
+ /**
66
+ * Return the ObjectLiteralExpression of the defineConfig call
67
+ */
68
+ #getDefineConfigObjectOrThrow(defineConfigCall) {
69
+ const configObject = defineConfigCall.getArguments()[0].asKindOrThrow(SyntaxKind.ObjectLiteralExpression);
70
+ return configObject;
71
+ }
72
+ /**
73
+ * Check if the defineConfig() call has the property assignment
74
+ * inside it or not. If not, it will create one and return it.
75
+ */
76
+ #getPropertyAssignmentInDefineConfigCall(propertyName, initializer) {
77
+ const file = this.#getRcFileOrThrow();
78
+ const defineConfigCall = this.#locateDefineConfigCallOrThrow(file);
79
+ const configObject = this.#getDefineConfigObjectOrThrow(defineConfigCall);
80
+ let property = configObject.getProperty(propertyName);
81
+ if (!property) {
82
+ configObject.addPropertyAssignment({ name: propertyName, initializer });
83
+ property = configObject.getProperty(propertyName);
84
+ }
85
+ return property;
86
+ }
87
+ /**
88
+ * Extract list of imported modules from an ArrayLiteralExpression
89
+ *
90
+ * It assumes that the array can have two types of elements:
91
+ *
92
+ * - Simple lazy imported modules: [() => import('path/to/file')]
93
+ * - Or an object entry: [{ file: () => import('path/to/file'), environment: ['web', 'console'] }]
94
+ * where the `file` property is a lazy imported module.
95
+ */
96
+ #extractModulesFromArray(array) {
97
+ const modules = array.getElements().map((element) => {
98
+ if (Node.isArrowFunction(element)) {
99
+ const importExp = element.getFirstDescendantByKindOrThrow(SyntaxKind.CallExpression);
100
+ const literal = importExp.getFirstDescendantByKindOrThrow(SyntaxKind.StringLiteral);
101
+ return literal.getLiteralValue();
102
+ }
103
+ if (Node.isObjectLiteralExpression(element)) {
104
+ const fileProp = element.getPropertyOrThrow("file");
105
+ const arrowFn = fileProp.getFirstDescendantByKindOrThrow(SyntaxKind.ArrowFunction);
106
+ const importExp = arrowFn.getFirstDescendantByKindOrThrow(SyntaxKind.CallExpression);
107
+ const literal = importExp.getFirstDescendantByKindOrThrow(SyntaxKind.StringLiteral);
108
+ return literal.getLiteralValue();
109
+ }
110
+ });
111
+ return modules.filter(Boolean);
112
+ }
113
+ /**
114
+ * Extract a specific property from an ArrayLiteralExpression
115
+ * that contains object entries.
116
+ *
117
+ * This function is mainly used for extractring the `pattern` property
118
+ * when adding a new meta files entry, or the `name` property when
119
+ * adding a new test suite.
120
+ */
121
+ #extractPropertyFromArray(array, propertyName) {
122
+ const property = array.getElements().map((el) => {
123
+ if (!Node.isObjectLiteralExpression(el))
124
+ return;
125
+ const nameProp = el.getPropertyOrThrow(propertyName);
126
+ if (!Node.isPropertyAssignment(nameProp))
127
+ return;
128
+ const name = nameProp.getInitializerIfKindOrThrow(SyntaxKind.StringLiteral);
129
+ return name.getLiteralValue();
130
+ });
131
+ return property.filter(Boolean);
132
+ }
133
+ /**
134
+ * Build a new module entry for the preloads and providers array
135
+ * based upon the environments specified
136
+ */
137
+ #buildNewModuleEntry(modulePath, environments) {
138
+ if (!this.#isInSpecificEnvironment(environments)) {
139
+ return `() => import('${modulePath}')`;
140
+ }
141
+ return `{
142
+ file: () => import('${modulePath}'),
143
+ environment: [${environments?.map((env) => `'${env}'`).join(", ")}],
144
+ }`;
145
+ }
146
+ /**
147
+ * Add a new command to the rcFile
148
+ */
149
+ addCommand(commandPath) {
150
+ const commandsProperty = this.#getPropertyAssignmentInDefineConfigCall("commands", "[]");
151
+ const commandsArray = commandsProperty.getInitializerIfKindOrThrow(
152
+ SyntaxKind.ArrayLiteralExpression
153
+ );
154
+ const commandString = `() => import('${commandPath}')`;
155
+ if (commandsArray.getElements().some((el) => el.getText() === commandString)) {
156
+ return this;
157
+ }
158
+ commandsArray.addElement(commandString);
159
+ return this;
160
+ }
161
+ /**
162
+ * Add a new preloaded file to the rcFile
163
+ */
164
+ addPreloadFile(modulePath, environments) {
165
+ const preloadsProperty = this.#getPropertyAssignmentInDefineConfigCall("preloads", "[]");
166
+ const preloadsArray = preloadsProperty.getInitializerIfKindOrThrow(
167
+ SyntaxKind.ArrayLiteralExpression
168
+ );
169
+ const existingPreloadedFiles = this.#extractModulesFromArray(preloadsArray);
170
+ const isDuplicate = existingPreloadedFiles.includes(modulePath);
171
+ if (isDuplicate) {
172
+ return this;
173
+ }
174
+ preloadsArray.addElement(this.#buildNewModuleEntry(modulePath, environments));
175
+ return this;
176
+ }
177
+ /**
178
+ * Add a new provider to the rcFile
179
+ */
180
+ addProvider(providerPath, environments) {
181
+ const property = this.#getPropertyAssignmentInDefineConfigCall("providers", "[]");
182
+ const providersArray = property.getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression);
183
+ const existingProviderPaths = this.#extractModulesFromArray(providersArray);
184
+ const isDuplicate = existingProviderPaths.includes(providerPath);
185
+ if (isDuplicate) {
186
+ return this;
187
+ }
188
+ providersArray.addElement(this.#buildNewModuleEntry(providerPath, environments));
189
+ return this;
190
+ }
191
+ /**
192
+ * Add a new meta file to the rcFile
193
+ */
194
+ addMetaFile(globPattern, reloadServer = false) {
195
+ const property = this.#getPropertyAssignmentInDefineConfigCall("metaFiles", "[]");
196
+ const metaFilesArray = property.getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression);
197
+ const alreadyDefinedPatterns = this.#extractPropertyFromArray(metaFilesArray, "pattern");
198
+ if (alreadyDefinedPatterns.includes(globPattern)) {
199
+ return this;
200
+ }
201
+ metaFilesArray.addElement(
202
+ `{
203
+ pattern: '${globPattern}',
204
+ reloadServer: ${reloadServer},
205
+ }`
206
+ );
207
+ return this;
208
+ }
209
+ /**
210
+ * Set directory name and path
211
+ */
212
+ setDirectory(key, value) {
213
+ const property = this.#getPropertyAssignmentInDefineConfigCall("directories", "{}");
214
+ const directories = property.getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression);
215
+ directories.addPropertyAssignment({ name: key, initializer: `'${value}'` });
216
+ return this;
217
+ }
218
+ /**
219
+ * Set command alias
220
+ */
221
+ setCommandAlias(alias, command) {
222
+ const aliasProperty = this.#getPropertyAssignmentInDefineConfigCall("commandsAliases", "{}");
223
+ const aliases = aliasProperty.getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression);
224
+ aliases.addPropertyAssignment({ name: alias, initializer: `'${command}'` });
225
+ return this;
226
+ }
227
+ /**
228
+ * Add a new test suite to the rcFile
229
+ */
230
+ addSuite(suiteName, files, timeout) {
231
+ const testProperty = this.#getPropertyAssignmentInDefineConfigCall(
232
+ "tests",
233
+ `{ suites: [], forceExit: true, timeout: 2000 }`
234
+ );
235
+ const property = testProperty.getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression).getPropertyOrThrow("suites");
236
+ const suitesArray = property.getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression);
237
+ const existingSuitesNames = this.#extractPropertyFromArray(suitesArray, "name");
238
+ if (existingSuitesNames.includes(suiteName)) {
239
+ return this;
240
+ }
241
+ const filesArray = Array.isArray(files) ? files : [files];
242
+ suitesArray.addElement(
243
+ `{
244
+ name: '${suiteName}',
245
+ files: [${filesArray.map((file) => `'${file}'`).join(", ")}],
246
+ timeout: ${timeout ?? 2e3},
247
+ }`
248
+ );
249
+ return this;
250
+ }
251
+ /**
252
+ * Save the adonisrc.ts file
253
+ */
254
+ save() {
255
+ const file = this.#getRcFileOrThrow();
256
+ file.formatText(this.#editorSettings);
257
+ return file.save();
258
+ }
259
+ };
260
+
261
+ // src/code_transformer/main.ts
262
+ var CodeTransformer = class {
263
+ /**
264
+ * Exporting utilities to install package and detect
265
+ * the package manager
266
+ */
267
+ installPackage = installPackage;
268
+ detectPackageManager = detectPackageManager;
269
+ /**
270
+ * Directory of the adonisjs project
271
+ */
272
+ #cwd;
273
+ /**
274
+ * The TsMorph project
275
+ */
276
+ #project;
277
+ /**
278
+ * Settings to use when persisting files
279
+ */
280
+ #editorSettings = {
281
+ indentSize: 2,
282
+ convertTabsToSpaces: true,
283
+ trimTrailingWhitespace: true,
284
+ ensureNewLineAtEndOfFile: true,
285
+ indentStyle: 2,
286
+ // @ts-expect-error SemicolonPreference doesn't seem to be re-exported from ts-morph
287
+ semicolons: "remove"
288
+ };
289
+ constructor(cwd) {
290
+ this.#cwd = cwd;
291
+ this.#project = new Project2({
292
+ tsConfigFilePath: join(fileURLToPath2(this.#cwd), "tsconfig.json"),
293
+ manipulationSettings: { quoteKind: QuoteKind.Single }
294
+ });
295
+ }
296
+ /**
297
+ * Add a new middleware to the middleware array of the
298
+ * given file
299
+ */
300
+ #addToMiddlewareArray(file, target, middlewareEntry) {
301
+ const callExpressions = file.getDescendantsOfKind(SyntaxKind2.CallExpression).filter((statement) => statement.getExpression().getText() === target);
302
+ if (!callExpressions.length) {
303
+ throw new Error(`Cannot find ${target} statement in the file.`);
304
+ }
305
+ const arrayLiteralExpression = callExpressions[0].getArguments()[0];
306
+ if (!arrayLiteralExpression || !Node2.isArrayLiteralExpression(arrayLiteralExpression)) {
307
+ throw new Error(`Cannot find middleware array in ${target} statement.`);
308
+ }
309
+ const middleware = `() => import('${middlewareEntry.path}')`;
310
+ const existingMiddlewareIndex = arrayLiteralExpression.getElements().findIndex((element) => element.getText() === middleware);
311
+ if (existingMiddlewareIndex === -1) {
312
+ if (middlewareEntry.position === "before") {
313
+ arrayLiteralExpression.insertElement(0, middleware);
314
+ } else {
315
+ arrayLiteralExpression.addElement(middleware);
316
+ }
317
+ }
318
+ }
319
+ /**
320
+ * Add a new middleware to the named middleware of the given file
321
+ */
322
+ #addToNamedMiddleware(file, middlewareEntry) {
323
+ if (!middlewareEntry.name) {
324
+ throw new Error("Named middleware requires a name.");
325
+ }
326
+ const callArguments = file.getVariableDeclarationOrThrow("middleware").getInitializerIfKindOrThrow(SyntaxKind2.CallExpression).getArguments();
327
+ if (callArguments.length === 0) {
328
+ throw new Error("Named middleware call has no arguments.");
329
+ }
330
+ const namedMiddlewareObject = callArguments[0];
331
+ if (!Node2.isObjectLiteralExpression(namedMiddlewareObject)) {
332
+ throw new Error("The argument of the named middleware call is not an object literal.");
333
+ }
334
+ const existingProperty = namedMiddlewareObject.getProperty(middlewareEntry.name);
335
+ if (!existingProperty) {
336
+ const middleware = `${middlewareEntry.name}: () => import('${middlewareEntry.path}')`;
337
+ namedMiddlewareObject.insertProperty(0, middleware);
338
+ }
339
+ }
340
+ /**
341
+ * Add a policy to the list of pre-registered policy
342
+ */
343
+ #addToPoliciesList(file, policyEntry) {
344
+ const policiesObject = file.getVariableDeclarationOrThrow("policies").getInitializerIfKindOrThrow(SyntaxKind2.ObjectLiteralExpression);
345
+ const existingProperty = policiesObject.getProperty(policyEntry.name);
346
+ if (!existingProperty) {
347
+ const policy = `${policyEntry.name}: () => import('${policyEntry.path}')`;
348
+ policiesObject.insertProperty(0, policy);
349
+ }
350
+ }
351
+ /**
352
+ * Write a leading comment
353
+ */
354
+ #addLeadingComment(writer, comment) {
355
+ if (!comment) {
356
+ return writer.blankLine();
357
+ }
358
+ return writer.blankLine().writeLine("/*").writeLine(`|----------------------------------------------------------`).writeLine(`| ${comment}`).writeLine(`|----------------------------------------------------------`).writeLine(`*/`);
359
+ }
360
+ /**
361
+ * Add new env variable validation in the
362
+ * `env.ts` file
363
+ */
364
+ async defineEnvValidations(definition) {
365
+ const kernelUrl = fileURLToPath2(new URL("./start/env.ts", this.#cwd));
366
+ const file = this.#project.getSourceFileOrThrow(kernelUrl);
367
+ const callExpressions = file.getDescendantsOfKind(SyntaxKind2.CallExpression).filter((statement) => statement.getExpression().getText() === "Env.create");
368
+ if (!callExpressions.length) {
369
+ throw new Error(`Cannot find Env.create statement in the file.`);
370
+ }
371
+ const objectLiteralExpression = callExpressions[0].getArguments()[1];
372
+ if (!Node2.isObjectLiteralExpression(objectLiteralExpression)) {
373
+ throw new Error(`The second argument of Env.create is not an object literal.`);
374
+ }
375
+ let shouldAddComment = true;
376
+ for (const [variable, validation] of Object.entries(definition.variables)) {
377
+ const existingProperty = objectLiteralExpression.getProperty(variable);
378
+ if (existingProperty) {
379
+ shouldAddComment = false;
380
+ }
381
+ if (!existingProperty) {
382
+ objectLiteralExpression.addPropertyAssignment({
383
+ name: variable,
384
+ initializer: validation,
385
+ leadingTrivia: (writer) => {
386
+ if (!shouldAddComment) {
387
+ return;
388
+ }
389
+ shouldAddComment = false;
390
+ return this.#addLeadingComment(writer, definition.leadingComment);
391
+ }
392
+ });
393
+ }
394
+ }
395
+ file.formatText(this.#editorSettings);
396
+ await file.save();
397
+ }
398
+ /**
399
+ * Define new middlewares inside the `start/kernel.ts`
400
+ * file
401
+ *
402
+ * This function is highly based on some assumptions
403
+ * and will not work if you significantly tweaked
404
+ * your `start/kernel.ts` file.
405
+ */
406
+ async addMiddlewareToStack(stack, middleware) {
407
+ const kernelUrl = fileURLToPath2(new URL("./start/kernel.ts", this.#cwd));
408
+ const file = this.#project.getSourceFileOrThrow(kernelUrl);
409
+ for (const middlewareEntry of middleware) {
410
+ if (stack === "named") {
411
+ this.#addToNamedMiddleware(file, middlewareEntry);
412
+ } else {
413
+ this.#addToMiddlewareArray(file, `${stack}.use`, middlewareEntry);
414
+ }
415
+ }
416
+ file.formatText(this.#editorSettings);
417
+ await file.save();
418
+ }
419
+ /**
420
+ * Update the `adonisrc.ts` file
421
+ */
422
+ async updateRcFile(callback) {
423
+ const rcFileTransformer = new RcFileTransformer(this.#cwd, this.#project);
424
+ callback(rcFileTransformer);
425
+ await rcFileTransformer.save();
426
+ }
427
+ /**
428
+ * Add a new Japa plugin in the `tests/bootstrap.ts` file
429
+ */
430
+ async addJapaPlugin(pluginCall, importDeclarations) {
431
+ const testBootstrapUrl = fileURLToPath2(new URL("./tests/bootstrap.ts", this.#cwd));
432
+ const file = this.#project.getSourceFileOrThrow(testBootstrapUrl);
433
+ const existingImports = file.getImportDeclarations();
434
+ importDeclarations.forEach((importDeclaration) => {
435
+ const existingImport = existingImports.find(
436
+ (mod) => mod.getModuleSpecifierValue() === importDeclaration.module
437
+ );
438
+ if (existingImport && importDeclaration.isNamed) {
439
+ if (!existingImport.getNamedImports().find((namedImport) => namedImport.getName() === importDeclaration.identifier)) {
440
+ existingImport.addNamedImport(importDeclaration.identifier);
441
+ }
442
+ return;
443
+ }
444
+ if (existingImport) {
445
+ return;
446
+ }
447
+ file.addImportDeclaration({
448
+ ...importDeclaration.isNamed ? { namedImports: [importDeclaration.identifier] } : { defaultImport: importDeclaration.identifier },
449
+ moduleSpecifier: importDeclaration.module
450
+ });
451
+ });
452
+ const pluginsArray = file.getVariableDeclaration("plugins")?.getInitializerIfKind(SyntaxKind2.ArrayLiteralExpression);
453
+ if (pluginsArray) {
454
+ if (!pluginsArray.getElements().find((element) => element.getText() === pluginCall)) {
455
+ pluginsArray.addElement(pluginCall);
456
+ }
457
+ }
458
+ file.formatText(this.#editorSettings);
459
+ await file.save();
460
+ }
461
+ /**
462
+ * Adds a policy to the list of `policies` object configured
463
+ * inside the `app/policies/main.ts` file.
464
+ */
465
+ async addPolicies(policies) {
466
+ const kernelUrl = fileURLToPath2(new URL("./app/policies/main.ts", this.#cwd));
467
+ const file = this.#project.getSourceFileOrThrow(kernelUrl);
468
+ for (const policy of policies) {
469
+ this.#addToPoliciesList(file, policy);
470
+ }
471
+ file.formatText(this.#editorSettings);
472
+ await file.save();
473
+ }
474
+ };
475
+ export {
476
+ CodeTransformer
477
+ };
478
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/code_transformer/main.ts","../../../src/code_transformer/rc_file_transformer.ts"],"sourcesContent":["/*\n * @adonisjs/assembler\n *\n * (c) AdonisJS\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nimport { join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { installPackage, detectPackageManager } from '@antfu/install-pkg'\nimport {\n Node,\n Project,\n QuoteKind,\n SourceFile,\n SyntaxKind,\n CodeBlockWriter,\n FormatCodeSettings,\n} from 'ts-morph'\n\nimport { RcFileTransformer } from './rc_file_transformer.js'\nimport type { MiddlewareNode, EnvValidationNode, BouncerPolicyNode } from '../types.js'\n\n/**\n * This class is responsible for updating\n */\nexport class CodeTransformer {\n /**\n * Exporting utilities to install package and detect\n * the package manager\n */\n installPackage = installPackage\n detectPackageManager = detectPackageManager\n\n /**\n * Directory of the adonisjs project\n */\n #cwd: URL\n\n /**\n * The TsMorph project\n */\n #project: Project\n\n /**\n * Settings to use when persisting files\n */\n #editorSettings: FormatCodeSettings = {\n indentSize: 2,\n convertTabsToSpaces: true,\n trimTrailingWhitespace: true,\n ensureNewLineAtEndOfFile: true,\n indentStyle: 2,\n // @ts-expect-error SemicolonPreference doesn't seem to be re-exported from ts-morph\n semicolons: 'remove',\n }\n\n constructor(cwd: URL) {\n this.#cwd = cwd\n this.#project = new Project({\n tsConfigFilePath: join(fileURLToPath(this.#cwd), 'tsconfig.json'),\n manipulationSettings: { quoteKind: QuoteKind.Single },\n })\n }\n\n /**\n * Add a new middleware to the middleware array of the\n * given file\n */\n #addToMiddlewareArray(file: SourceFile, target: string, middlewareEntry: MiddlewareNode) {\n const callExpressions = file\n .getDescendantsOfKind(SyntaxKind.CallExpression)\n .filter((statement) => statement.getExpression().getText() === target)\n\n if (!callExpressions.length) {\n throw new Error(`Cannot find ${target} statement in the file.`)\n }\n\n const arrayLiteralExpression = callExpressions[0].getArguments()[0]\n if (!arrayLiteralExpression || !Node.isArrayLiteralExpression(arrayLiteralExpression)) {\n throw new Error(`Cannot find middleware array in ${target} statement.`)\n }\n\n const middleware = `() => import('${middlewareEntry.path}')`\n\n /**\n * Delete the existing middleware if it exists\n */\n const existingMiddlewareIndex = arrayLiteralExpression\n .getElements()\n .findIndex((element) => element.getText() === middleware)\n\n if (existingMiddlewareIndex === -1) {\n /**\n * Add the middleware to the top or bottom of the array\n */\n if (middlewareEntry.position === 'before') {\n arrayLiteralExpression.insertElement(0, middleware)\n } else {\n arrayLiteralExpression.addElement(middleware)\n }\n }\n }\n\n /**\n * Add a new middleware to the named middleware of the given file\n */\n #addToNamedMiddleware(file: SourceFile, middlewareEntry: MiddlewareNode) {\n if (!middlewareEntry.name) {\n throw new Error('Named middleware requires a name.')\n }\n\n const callArguments = file\n .getVariableDeclarationOrThrow('middleware')\n .getInitializerIfKindOrThrow(SyntaxKind.CallExpression)\n .getArguments()\n\n if (callArguments.length === 0) {\n throw new Error('Named middleware call has no arguments.')\n }\n\n const namedMiddlewareObject = callArguments[0]\n if (!Node.isObjectLiteralExpression(namedMiddlewareObject)) {\n throw new Error('The argument of the named middleware call is not an object literal.')\n }\n\n /**\n * Check if property is already defined. If so, remove it\n */\n const existingProperty = namedMiddlewareObject.getProperty(middlewareEntry.name)\n if (!existingProperty) {\n /**\n * Add the named middleware\n */\n const middleware = `${middlewareEntry.name}: () => import('${middlewareEntry.path}')`\n namedMiddlewareObject!.insertProperty(0, middleware)\n }\n }\n\n /**\n * Add a policy to the list of pre-registered policy\n */\n #addToPoliciesList(file: SourceFile, policyEntry: BouncerPolicyNode) {\n const policiesObject = file\n .getVariableDeclarationOrThrow('policies')\n .getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression)\n\n /**\n * Only define policy when one with the existing name does not\n * exist.\n */\n const existingProperty = policiesObject.getProperty(policyEntry.name)\n if (!existingProperty) {\n const policy = `${policyEntry.name}: () => import('${policyEntry.path}')`\n policiesObject!.insertProperty(0, policy)\n }\n }\n\n /**\n * Write a leading comment\n */\n #addLeadingComment(writer: CodeBlockWriter, comment?: string) {\n if (!comment) {\n return writer.blankLine()\n }\n\n return writer\n .blankLine()\n .writeLine('/*')\n .writeLine(`|----------------------------------------------------------`)\n .writeLine(`| ${comment}`)\n .writeLine(`|----------------------------------------------------------`)\n .writeLine(`*/`)\n }\n\n /**\n * Add new env variable validation in the\n * `env.ts` file\n */\n async defineEnvValidations(definition: EnvValidationNode) {\n /**\n * Get the `start/env.ts` source file\n */\n const kernelUrl = fileURLToPath(new URL('./start/env.ts', this.#cwd))\n const file = this.#project.getSourceFileOrThrow(kernelUrl)\n\n /**\n * Get the `Env.create` call expression\n */\n const callExpressions = file\n .getDescendantsOfKind(SyntaxKind.CallExpression)\n .filter((statement) => statement.getExpression().getText() === 'Env.create')\n\n if (!callExpressions.length) {\n throw new Error(`Cannot find Env.create statement in the file.`)\n }\n\n const objectLiteralExpression = callExpressions[0].getArguments()[1]\n if (!Node.isObjectLiteralExpression(objectLiteralExpression)) {\n throw new Error(`The second argument of Env.create is not an object literal.`)\n }\n\n let shouldAddComment = true\n\n /**\n * Add each variable validation\n */\n for (const [variable, validation] of Object.entries(definition.variables)) {\n /**\n * Check if the variable is already defined. If so, remove it\n */\n const existingProperty = objectLiteralExpression.getProperty(variable)\n\n /**\n * Do not add leading comment if one or more properties\n * already exists\n */\n if (existingProperty) {\n shouldAddComment = false\n }\n\n /**\n * Add property only when the property does not exist\n */\n if (!existingProperty) {\n objectLiteralExpression.addPropertyAssignment({\n name: variable,\n initializer: validation,\n leadingTrivia: (writer) => {\n if (!shouldAddComment) {\n return\n }\n\n shouldAddComment = false\n return this.#addLeadingComment(writer, definition.leadingComment)\n },\n })\n }\n }\n\n file.formatText(this.#editorSettings)\n await file.save()\n }\n\n /**\n * Define new middlewares inside the `start/kernel.ts`\n * file\n *\n * This function is highly based on some assumptions\n * and will not work if you significantly tweaked\n * your `start/kernel.ts` file.\n */\n async addMiddlewareToStack(stack: 'server' | 'router' | 'named', middleware: MiddlewareNode[]) {\n /**\n * Get the `start/kernel.ts` source file\n */\n const kernelUrl = fileURLToPath(new URL('./start/kernel.ts', this.#cwd))\n const file = this.#project.getSourceFileOrThrow(kernelUrl)\n\n /**\n * Process each middleware entry\n */\n for (const middlewareEntry of middleware) {\n if (stack === 'named') {\n this.#addToNamedMiddleware(file, middlewareEntry)\n } else {\n this.#addToMiddlewareArray(file!, `${stack}.use`, middlewareEntry)\n }\n }\n\n file.formatText(this.#editorSettings)\n await file.save()\n }\n\n /**\n * Update the `adonisrc.ts` file\n */\n async updateRcFile(callback: (transformer: RcFileTransformer) => void) {\n const rcFileTransformer = new RcFileTransformer(this.#cwd, this.#project)\n callback(rcFileTransformer)\n await rcFileTransformer.save()\n }\n\n /**\n * Add a new Japa plugin in the `tests/bootstrap.ts` file\n */\n async addJapaPlugin(\n pluginCall: string,\n importDeclarations: { isNamed: boolean; module: string; identifier: string }[]\n ) {\n /**\n * Get the `tests/bootstrap.ts` source file\n */\n const testBootstrapUrl = fileURLToPath(new URL('./tests/bootstrap.ts', this.#cwd))\n const file = this.#project.getSourceFileOrThrow(testBootstrapUrl)\n\n /**\n * Add the import declaration\n */\n const existingImports = file.getImportDeclarations()\n\n importDeclarations.forEach((importDeclaration) => {\n const existingImport = existingImports.find(\n (mod) => mod.getModuleSpecifierValue() === importDeclaration.module\n )\n\n /**\n * Add a new named import to existing import for the\n * same module\n */\n if (existingImport && importDeclaration.isNamed) {\n if (\n !existingImport\n .getNamedImports()\n .find((namedImport) => namedImport.getName() === importDeclaration.identifier)\n ) {\n existingImport.addNamedImport(importDeclaration.identifier)\n }\n return\n }\n\n /**\n * Ignore default import when the same module is already imported.\n * The chances are the existing default import and the importDeclaration\n * identifiers are not the same. But we should not modify existing source\n */\n if (existingImport) {\n return\n }\n\n file.addImportDeclaration({\n ...(importDeclaration.isNamed\n ? { namedImports: [importDeclaration.identifier] }\n : { defaultImport: importDeclaration.identifier }),\n moduleSpecifier: importDeclaration.module,\n })\n })\n\n /**\n * Insert the plugin call in the `plugins` array\n */\n const pluginsArray = file\n .getVariableDeclaration('plugins')\n ?.getInitializerIfKind(SyntaxKind.ArrayLiteralExpression)\n\n /**\n * Add plugin call to the plugins array\n */\n if (pluginsArray) {\n if (!pluginsArray.getElements().find((element) => element.getText() === pluginCall)) {\n pluginsArray.addElement(pluginCall)\n }\n }\n\n file.formatText(this.#editorSettings)\n await file.save()\n }\n\n /**\n * Adds a policy to the list of `policies` object configured\n * inside the `app/policies/main.ts` file.\n */\n async addPolicies(policies: BouncerPolicyNode[]) {\n /**\n * Get the `app/policies/main.ts` source file\n */\n const kernelUrl = fileURLToPath(new URL('./app/policies/main.ts', this.#cwd))\n const file = this.#project.getSourceFileOrThrow(kernelUrl)\n\n /**\n * Process each middleware entry\n */\n for (const policy of policies) {\n this.#addToPoliciesList(file, policy)\n }\n\n file.formatText(this.#editorSettings)\n await file.save()\n }\n}\n","/*\n * @adonisjs/assembler\n *\n * (c) AdonisJS\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nimport { fileURLToPath } from 'node:url'\nimport type { AppEnvironments } from '@adonisjs/application/types'\nimport {\n Node,\n Project,\n SourceFile,\n SyntaxKind,\n CallExpression,\n PropertyAssignment,\n FormatCodeSettings,\n ArrayLiteralExpression,\n} from 'ts-morph'\n\n/**\n * RcFileTransformer is used to transform the `adonisrc.ts` file\n * for adding new commands, providers, meta files etc\n */\nexport class RcFileTransformer {\n #cwd: URL\n #project: Project\n\n /**\n * Settings to use when persisting files\n */\n #editorSettings: FormatCodeSettings = {\n indentSize: 2,\n convertTabsToSpaces: true,\n trimTrailingWhitespace: true,\n ensureNewLineAtEndOfFile: true,\n indentStyle: 2,\n // @ts-expect-error SemicolonPreference doesn't seem to be re-exported from ts-morph\n semicolons: 'remove',\n }\n\n constructor(cwd: URL, project: Project) {\n this.#cwd = cwd\n this.#project = project\n }\n\n /**\n * Get the `adonisrc.ts` source file\n */\n #getRcFileOrThrow() {\n const kernelUrl = fileURLToPath(new URL('./adonisrc.ts', this.#cwd))\n return this.#project.getSourceFileOrThrow(kernelUrl)\n }\n\n /**\n * Check if environments array has a subset of available environments\n */\n #isInSpecificEnvironment(environments?: AppEnvironments[]): boolean {\n if (!environments) {\n return false\n }\n\n return !!(['web', 'console', 'test', 'repl'] as const).find(\n (env) => !environments.includes(env)\n )\n }\n\n /**\n * Locate the `defineConfig` call inside the `adonisrc.ts` file\n */\n #locateDefineConfigCallOrThrow(file: SourceFile) {\n const call = file\n .getDescendantsOfKind(SyntaxKind.CallExpression)\n .find((statement) => statement.getExpression().getText() === 'defineConfig')\n\n if (!call) {\n throw new Error('Could not locate the defineConfig call.')\n }\n\n return call\n }\n\n /**\n * Return the ObjectLiteralExpression of the defineConfig call\n */\n #getDefineConfigObjectOrThrow(defineConfigCall: CallExpression) {\n const configObject = defineConfigCall\n .getArguments()[0]\n .asKindOrThrow(SyntaxKind.ObjectLiteralExpression)\n\n return configObject\n }\n\n /**\n * Check if the defineConfig() call has the property assignment\n * inside it or not. If not, it will create one and return it.\n */\n #getPropertyAssignmentInDefineConfigCall(propertyName: string, initializer: string) {\n const file = this.#getRcFileOrThrow()\n const defineConfigCall = this.#locateDefineConfigCallOrThrow(file)\n const configObject = this.#getDefineConfigObjectOrThrow(defineConfigCall)\n\n let property = configObject.getProperty(propertyName)\n\n if (!property) {\n configObject.addPropertyAssignment({ name: propertyName, initializer })\n property = configObject.getProperty(propertyName)\n }\n\n return property as PropertyAssignment\n }\n\n /**\n * Extract list of imported modules from an ArrayLiteralExpression\n *\n * It assumes that the array can have two types of elements:\n *\n * - Simple lazy imported modules: [() => import('path/to/file')]\n * - Or an object entry: [{ file: () => import('path/to/file'), environment: ['web', 'console'] }]\n * where the `file` property is a lazy imported module.\n */\n #extractModulesFromArray(array: ArrayLiteralExpression) {\n const modules = array.getElements().map((element) => {\n /**\n * Simple lazy imported module\n */\n if (Node.isArrowFunction(element)) {\n const importExp = element.getFirstDescendantByKindOrThrow(SyntaxKind.CallExpression)\n const literal = importExp.getFirstDescendantByKindOrThrow(SyntaxKind.StringLiteral)\n return literal.getLiteralValue()\n }\n\n /**\n * Object entry\n */\n if (Node.isObjectLiteralExpression(element)) {\n const fileProp = element.getPropertyOrThrow('file') as PropertyAssignment\n const arrowFn = fileProp.getFirstDescendantByKindOrThrow(SyntaxKind.ArrowFunction)\n const importExp = arrowFn.getFirstDescendantByKindOrThrow(SyntaxKind.CallExpression)\n const literal = importExp.getFirstDescendantByKindOrThrow(SyntaxKind.StringLiteral)\n return literal.getLiteralValue()\n }\n })\n\n return modules.filter(Boolean) as string[]\n }\n\n /**\n * Extract a specific property from an ArrayLiteralExpression\n * that contains object entries.\n *\n * This function is mainly used for extractring the `pattern` property\n * when adding a new meta files entry, or the `name` property when\n * adding a new test suite.\n */\n #extractPropertyFromArray(array: ArrayLiteralExpression, propertyName: string) {\n const property = array.getElements().map((el) => {\n if (!Node.isObjectLiteralExpression(el)) return\n\n const nameProp = el.getPropertyOrThrow(propertyName)\n if (!Node.isPropertyAssignment(nameProp)) return\n\n const name = nameProp.getInitializerIfKindOrThrow(SyntaxKind.StringLiteral)\n return name.getLiteralValue()\n })\n\n return property.filter(Boolean) as string[]\n }\n\n /**\n * Build a new module entry for the preloads and providers array\n * based upon the environments specified\n */\n #buildNewModuleEntry(modulePath: string, environments?: AppEnvironments[]) {\n if (!this.#isInSpecificEnvironment(environments)) {\n return `() => import('${modulePath}')`\n }\n\n return `{\n file: () => import('${modulePath}'),\n environment: [${environments?.map((env) => `'${env}'`).join(', ')}],\n }`\n }\n\n /**\n * Add a new command to the rcFile\n */\n addCommand(commandPath: string) {\n const commandsProperty = this.#getPropertyAssignmentInDefineConfigCall('commands', '[]')\n const commandsArray = commandsProperty.getInitializerIfKindOrThrow(\n SyntaxKind.ArrayLiteralExpression\n )\n\n const commandString = `() => import('${commandPath}')`\n\n /**\n * If the command already exists, do nothing\n */\n if (commandsArray.getElements().some((el) => el.getText() === commandString)) {\n return this\n }\n\n /**\n * Add the command to the array\n */\n commandsArray.addElement(commandString)\n return this\n }\n\n /**\n * Add a new preloaded file to the rcFile\n */\n addPreloadFile(modulePath: string, environments?: AppEnvironments[]) {\n const preloadsProperty = this.#getPropertyAssignmentInDefineConfigCall('preloads', '[]')\n const preloadsArray = preloadsProperty.getInitializerIfKindOrThrow(\n SyntaxKind.ArrayLiteralExpression\n )\n\n /**\n * Check for duplicates\n */\n const existingPreloadedFiles = this.#extractModulesFromArray(preloadsArray)\n const isDuplicate = existingPreloadedFiles.includes(modulePath)\n if (isDuplicate) {\n return this\n }\n\n /**\n * Add the preloaded file to the array\n */\n preloadsArray.addElement(this.#buildNewModuleEntry(modulePath, environments))\n return this\n }\n\n /**\n * Add a new provider to the rcFile\n */\n addProvider(providerPath: string, environments?: AppEnvironments[]) {\n const property = this.#getPropertyAssignmentInDefineConfigCall('providers', '[]')\n const providersArray = property.getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression)\n\n /**\n * Check for duplicates\n */\n const existingProviderPaths = this.#extractModulesFromArray(providersArray)\n const isDuplicate = existingProviderPaths.includes(providerPath)\n if (isDuplicate) {\n return this\n }\n\n /**\n * Add the provider to the array\n */\n providersArray.addElement(this.#buildNewModuleEntry(providerPath, environments))\n\n return this\n }\n\n /**\n * Add a new meta file to the rcFile\n */\n addMetaFile(globPattern: string, reloadServer = false) {\n const property = this.#getPropertyAssignmentInDefineConfigCall('metaFiles', '[]')\n const metaFilesArray = property.getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression)\n\n /**\n * Check for duplicates\n */\n const alreadyDefinedPatterns = this.#extractPropertyFromArray(metaFilesArray, 'pattern')\n if (alreadyDefinedPatterns.includes(globPattern)) {\n return this\n }\n\n /**\n * Add the meta file to the array\n */\n metaFilesArray.addElement(\n `{\n pattern: '${globPattern}',\n reloadServer: ${reloadServer},\n }`\n )\n\n return this\n }\n\n /**\n * Set directory name and path\n */\n setDirectory(key: string, value: string) {\n const property = this.#getPropertyAssignmentInDefineConfigCall('directories', '{}')\n const directories = property.getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression)\n directories.addPropertyAssignment({ name: key, initializer: `'${value}'` })\n\n return this\n }\n\n /**\n * Set command alias\n */\n setCommandAlias(alias: string, command: string) {\n const aliasProperty = this.#getPropertyAssignmentInDefineConfigCall('commandsAliases', '{}')\n const aliases = aliasProperty.getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression)\n aliases.addPropertyAssignment({ name: alias, initializer: `'${command}'` })\n\n return this\n }\n\n /**\n * Add a new test suite to the rcFile\n */\n addSuite(suiteName: string, files: string | string[], timeout?: number) {\n const testProperty = this.#getPropertyAssignmentInDefineConfigCall(\n 'tests',\n `{ suites: [], forceExit: true, timeout: 2000 }`\n )\n\n const property = testProperty\n .getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression)\n .getPropertyOrThrow('suites') as PropertyAssignment\n\n const suitesArray = property.getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression)\n\n /**\n * Check for duplicates\n */\n const existingSuitesNames = this.#extractPropertyFromArray(suitesArray, 'name')\n if (existingSuitesNames.includes(suiteName)) {\n return this\n }\n\n /**\n * Add the suite to the array\n */\n const filesArray = Array.isArray(files) ? files : [files]\n suitesArray.addElement(\n `{\n name: '${suiteName}',\n files: [${filesArray.map((file) => `'${file}'`).join(', ')}],\n timeout: ${timeout ?? 2000},\n }`\n )\n\n return this\n }\n\n /**\n * Save the adonisrc.ts file\n */\n save() {\n const file = this.#getRcFileOrThrow()\n file.formatText(this.#editorSettings)\n return file.save()\n }\n}\n"],"mappings":"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","names":["fileURLToPath","Node","Project","SyntaxKind","Project","fileURLToPath","SyntaxKind","Node"]}
@@ -0,0 +1,43 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import type { AppEnvironments } from '@adonisjs/application/types';
3
+ import { Project } from 'ts-morph';
4
+ /**
5
+ * RcFileTransformer is used to transform the `adonisrc.ts` file
6
+ * for adding new commands, providers, meta files etc
7
+ */
8
+ export declare class RcFileTransformer {
9
+ #private;
10
+ constructor(cwd: URL, project: Project);
11
+ /**
12
+ * Add a new command to the rcFile
13
+ */
14
+ addCommand(commandPath: string): this;
15
+ /**
16
+ * Add a new preloaded file to the rcFile
17
+ */
18
+ addPreloadFile(modulePath: string, environments?: AppEnvironments[]): this;
19
+ /**
20
+ * Add a new provider to the rcFile
21
+ */
22
+ addProvider(providerPath: string, environments?: AppEnvironments[]): this;
23
+ /**
24
+ * Add a new meta file to the rcFile
25
+ */
26
+ addMetaFile(globPattern: string, reloadServer?: boolean): this;
27
+ /**
28
+ * Set directory name and path
29
+ */
30
+ setDirectory(key: string, value: string): this;
31
+ /**
32
+ * Set command alias
33
+ */
34
+ setCommandAlias(alias: string, command: string): this;
35
+ /**
36
+ * Add a new test suite to the rcFile
37
+ */
38
+ addSuite(suiteName: string, files: string | string[], timeout?: number): this;
39
+ /**
40
+ * Save the adonisrc.ts file
41
+ */
42
+ save(): Promise<void>;
43
+ }
@@ -0,0 +1,3 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ declare const _default: import("util").DebugLogger;
3
+ export default _default;
@@ -8,9 +8,9 @@ import type { DevServerOptions } from './types.js';
8
8
  *
9
9
  * The Dev server performs the following actions
10
10
  *
11
- * - Assigns a random PORT, when PORT inside .env file is in use
11
+ * - Assigns a random PORT, when PORT inside .env file is in use.
12
12
  * - Uses tsconfig.json file to collect a list of files to watch.
13
- * - Uses metaFiles from .adonisrc.json file to collect a list of files to watch.
13
+ * - Uses metaFiles from adonisrc.ts file to collect a list of files to watch.
14
14
  * - Restart HTTP server on every file change.
15
15
  */
16
16
  export declare class DevServer {
@@ -26,10 +26,6 @@ export declare function watch(cwd: string | URL, ts: typeof tsStatic, options: W
26
26
  * Check if file is an .env file
27
27
  */
28
28
  export declare function isDotEnvFile(filePath: string): boolean;
29
- /**
30
- * Check if file is .adonisrc.json file
31
- */
32
- export declare function isRcFile(filePath: string): boolean;
33
29
  /**
34
30
  * Returns the port to use after inspect the dot-env files inside
35
31
  * a given directory.
@@ -39,7 +35,12 @@ export declare function isRcFile(filePath: string): boolean;
39
35
  *
40
36
  * - The "process.env.PORT" value is used if exists.
41
37
  * - The dot-env files are loaded using the "EnvLoader" and the PORT
42
- * value is by iterating over all the loaded files. The iteration
43
- * stops after first find.
38
+ * value is used by iterating over all the loaded files. The
39
+ * iteration stops after first find.
44
40
  */
45
41
  export declare function getPort(cwd: URL): Promise<number>;
42
+ /**
43
+ * Helper function to copy files from relative paths or glob
44
+ * patterns
45
+ */
46
+ export declare function copyFiles(files: string[], cwd: string, outDir: string): Promise<void[]>;
@@ -3,15 +3,16 @@ import type tsStatic from 'typescript';
3
3
  import { type Logger } from '@poppinss/cliui';
4
4
  import type { TestRunnerOptions } from './types.js';
5
5
  /**
6
- * Exposes the API to start the development. Optionally, the watch API can be
7
- * used to watch for file changes and restart the development server.
6
+ * Exposes the API to run Japa tests and optionally watch for file
7
+ * changes to re-run the tests.
8
8
  *
9
- * The Dev server performs the following actions
9
+ * The watch mode functions as follows.
10
+ *
11
+ * - If the changed file is a test file, then only tests for that file
12
+ * will be re-run.
13
+ * - Otherwise, all tests will re-run with respect to the initial
14
+ * filters applied when running the `node ace test` command.
10
15
  *
11
- * - Assigns a random PORT, when PORT inside .env file is in use
12
- * - Uses tsconfig.json file to collect a list of files to watch.
13
- * - Uses metaFiles from .adonisrc.json file to collect a list of files to watch.
14
- * - Restart HTTP server on every file change.
15
16
  */
16
17
  export declare class TestRunner {
17
18
  #private;