@adonisjs/assembler 6.1.3-2 → 6.1.3-20

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,43 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { Project } from 'ts-morph';
3
+ import type { AppEnvironments } from '@adonisjs/application/types';
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,272 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import { Node, SyntaxKind, } from 'ts-morph';
3
+ /**
4
+ * RcFileTransformer is used to transform the `adonisrc.ts` file
5
+ * for adding new commands, providers, meta files etc
6
+ */
7
+ export class RcFileTransformer {
8
+ #cwd;
9
+ #project;
10
+ /**
11
+ * Settings to use when persisting files
12
+ */
13
+ #editorSettings = {
14
+ indentSize: 2,
15
+ convertTabsToSpaces: true,
16
+ trimTrailingWhitespace: true,
17
+ };
18
+ constructor(cwd, project) {
19
+ this.#cwd = cwd;
20
+ this.#project = project;
21
+ }
22
+ /**
23
+ * Get the `adonisrc.ts` source file
24
+ */
25
+ #getRcFileOrThrow() {
26
+ const kernelUrl = fileURLToPath(new URL('./adonisrc.ts', this.#cwd));
27
+ return this.#project.getSourceFileOrThrow(kernelUrl);
28
+ }
29
+ /**
30
+ * Check if environments array has a subset of available environments
31
+ */
32
+ #isInSpecificEnvironment(environments) {
33
+ if (!environments)
34
+ return false;
35
+ return !!['web', 'console', 'test', 'repl'].find((env) => !environments.includes(env));
36
+ }
37
+ /**
38
+ * Locate the `defineConfig` call inside the `adonisrc.ts` file
39
+ */
40
+ #locateDefineConfigCallOrThrow(file) {
41
+ const call = file
42
+ .getDescendantsOfKind(SyntaxKind.CallExpression)
43
+ .find((statement) => statement.getExpression().getText() === 'defineConfig');
44
+ if (!call) {
45
+ throw new Error('Could not locate the defineConfig call.');
46
+ }
47
+ return call;
48
+ }
49
+ /**
50
+ * Return the ObjectLiteralExpression of the defineConfig call
51
+ */
52
+ #getDefineConfigObjectOrThrow(defineConfigCall) {
53
+ const configObject = defineConfigCall
54
+ .getArguments()[0]
55
+ .asKindOrThrow(SyntaxKind.ObjectLiteralExpression);
56
+ return configObject;
57
+ }
58
+ /**
59
+ * Check if the defineConfig() call has the property assignment
60
+ * inside it or not. If not, it will create one and return it.
61
+ */
62
+ #getPropertyAssignmentInDefineConfigCall(propertyName, initializer) {
63
+ const file = this.#getRcFileOrThrow();
64
+ const defineConfigCall = this.#locateDefineConfigCallOrThrow(file);
65
+ const configObject = this.#getDefineConfigObjectOrThrow(defineConfigCall);
66
+ let property = configObject.getProperty(propertyName);
67
+ if (!property) {
68
+ configObject.addPropertyAssignment({ name: propertyName, initializer });
69
+ property = configObject.getProperty(propertyName);
70
+ }
71
+ return property;
72
+ }
73
+ /**
74
+ * Extract list of imported modules from an ArrayLiteralExpression
75
+ *
76
+ * It assumes that the array can have two types of elements:
77
+ *
78
+ * - Simple lazy imported modules: [() => import('path/to/file')]
79
+ * - Or an object entry: [{ file: () => import('path/to/file'), environment: ['web', 'console'] }]
80
+ * where the `file` property is a lazy imported module.
81
+ */
82
+ #extractModulesFromArray(array) {
83
+ const modules = array.getElements().map((element) => {
84
+ /**
85
+ * Simple lazy imported module
86
+ */
87
+ if (Node.isArrowFunction(element)) {
88
+ const importExp = element.getFirstDescendantByKindOrThrow(SyntaxKind.CallExpression);
89
+ const literal = importExp.getFirstDescendantByKindOrThrow(SyntaxKind.StringLiteral);
90
+ return literal.getLiteralValue();
91
+ }
92
+ /**
93
+ * Object entry
94
+ */
95
+ if (Node.isObjectLiteralExpression(element)) {
96
+ const fileProp = element.getPropertyOrThrow('file');
97
+ const arrowFn = fileProp.getFirstDescendantByKindOrThrow(SyntaxKind.ArrowFunction);
98
+ const importExp = arrowFn.getFirstDescendantByKindOrThrow(SyntaxKind.CallExpression);
99
+ const literal = importExp.getFirstDescendantByKindOrThrow(SyntaxKind.StringLiteral);
100
+ return literal.getLiteralValue();
101
+ }
102
+ });
103
+ return modules.filter(Boolean);
104
+ }
105
+ /**
106
+ * Extract a specific property from an ArrayLiteralExpression
107
+ * that contains object entries.
108
+ *
109
+ * This function is mainly used for extractring the `pattern` property
110
+ * when adding a new meta files entry, or the `name` property when
111
+ * adding a new test suite.
112
+ */
113
+ #extractPropertyFromArray(array, propertyName) {
114
+ const property = array.getElements().map((el) => {
115
+ if (!Node.isObjectLiteralExpression(el))
116
+ return;
117
+ const nameProp = el.getPropertyOrThrow(propertyName);
118
+ if (!Node.isPropertyAssignment(nameProp))
119
+ return;
120
+ const name = nameProp.getInitializerIfKindOrThrow(SyntaxKind.StringLiteral);
121
+ return name.getLiteralValue();
122
+ });
123
+ return property.filter(Boolean);
124
+ }
125
+ /**
126
+ * Build a new module entry for the preloads and providers array
127
+ * based upon the environments specified
128
+ */
129
+ #buildNewModuleEntry(modulePath, environments) {
130
+ if (!this.#isInSpecificEnvironment(environments)) {
131
+ return `() => import('${modulePath}')`;
132
+ }
133
+ return `{
134
+ file: () => import('${modulePath}'),
135
+ environment: [${environments?.map((env) => `'${env}'`).join(', ')}],
136
+ }`;
137
+ }
138
+ /**
139
+ * Add a new command to the rcFile
140
+ */
141
+ addCommand(commandPath) {
142
+ const commandsProperty = this.#getPropertyAssignmentInDefineConfigCall('providers', '[]');
143
+ const commandsArray = commandsProperty.getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression);
144
+ const commandString = `() => import('${commandPath}')`;
145
+ /**
146
+ * If the command already exists, do nothing
147
+ */
148
+ if (commandsArray.getElements().some((el) => el.getText() === commandString)) {
149
+ return this;
150
+ }
151
+ /**
152
+ * Add the command to the array
153
+ */
154
+ commandsArray.addElement(commandString);
155
+ return this;
156
+ }
157
+ /**
158
+ * Add a new preloaded file to the rcFile
159
+ */
160
+ addPreloadFile(modulePath, environments) {
161
+ const preloadsProperty = this.#getPropertyAssignmentInDefineConfigCall('preloads', '[]');
162
+ const preloadsArray = preloadsProperty.getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression);
163
+ /**
164
+ * Check for duplicates
165
+ */
166
+ const existingPreloadedFiles = this.#extractModulesFromArray(preloadsArray);
167
+ const isDuplicate = existingPreloadedFiles.includes(modulePath);
168
+ if (isDuplicate) {
169
+ return this;
170
+ }
171
+ /**
172
+ * Add the preloaded file to the array
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
+ /**
184
+ * Check for duplicates
185
+ */
186
+ const existingProviderPaths = this.#extractModulesFromArray(providersArray);
187
+ const isDuplicate = existingProviderPaths.includes(providerPath);
188
+ if (isDuplicate) {
189
+ return this;
190
+ }
191
+ /**
192
+ * Add the provider to the array
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
+ /**
204
+ * Check for duplicates
205
+ */
206
+ const alreadyDefinedPatterns = this.#extractPropertyFromArray(metaFilesArray, 'pattern');
207
+ if (alreadyDefinedPatterns.includes(globPattern)) {
208
+ return this;
209
+ }
210
+ /**
211
+ * Add the meta file to the array
212
+ */
213
+ metaFilesArray.addElement(`{
214
+ pattern: '${globPattern}',
215
+ reloadServer: ${reloadServer},
216
+ }`);
217
+ return this;
218
+ }
219
+ /**
220
+ * Set directory name and path
221
+ */
222
+ setDirectory(key, value) {
223
+ const property = this.#getPropertyAssignmentInDefineConfigCall('directories', '{}');
224
+ const directories = property.getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression);
225
+ directories.addPropertyAssignment({ name: key, initializer: `'${value}'` });
226
+ return this;
227
+ }
228
+ /**
229
+ * Set command alias
230
+ */
231
+ setCommandAlias(alias, command) {
232
+ const aliasProperty = this.#getPropertyAssignmentInDefineConfigCall('commandsAliases', '{}');
233
+ const aliases = aliasProperty.getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression);
234
+ aliases.addPropertyAssignment({ name: alias, initializer: `'${command}'` });
235
+ return this;
236
+ }
237
+ /**
238
+ * Add a new test suite to the rcFile
239
+ */
240
+ addSuite(suiteName, files, timeout) {
241
+ const testProperty = this.#getPropertyAssignmentInDefineConfigCall('tests', `{ suites: [], forceExit: true, timeout: 2000 }`);
242
+ const property = testProperty
243
+ .getInitializerIfKindOrThrow(SyntaxKind.ObjectLiteralExpression)
244
+ .getPropertyOrThrow('suites');
245
+ const suitesArray = property.getInitializerIfKindOrThrow(SyntaxKind.ArrayLiteralExpression);
246
+ /**
247
+ * Check for duplicates
248
+ */
249
+ const existingSuitesNames = this.#extractPropertyFromArray(suitesArray, 'name');
250
+ if (existingSuitesNames.includes(suiteName)) {
251
+ return this;
252
+ }
253
+ /**
254
+ * Add the suite to the array
255
+ */
256
+ const filesArray = Array.isArray(files) ? files : [files];
257
+ suitesArray.addElement(`{
258
+ name: '${suiteName}',
259
+ files: [${filesArray.map((file) => `'${file}'`).join(', ')}],
260
+ timeout: ${timeout ?? 2000},
261
+ }`);
262
+ return this;
263
+ }
264
+ /**
265
+ * Save the adonisrc.ts file
266
+ */
267
+ save() {
268
+ const file = this.#getRcFileOrThrow();
269
+ file.formatText(this.#editorSettings);
270
+ return file.save();
271
+ }
272
+ }
@@ -0,0 +1,3 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ declare const _default: import("util").DebugLogger;
3
+ export default _default;
@@ -0,0 +1,10 @@
1
+ /*
2
+ * @adonisjs/assembler
3
+ *
4
+ * (c) AdonisJS
5
+ *
6
+ * For the full copyright and license information, please view the LICENSE
7
+ * file that was distributed with this source code.
8
+ */
9
+ import { debuglog } from 'node:util';
10
+ export default debuglog('adonisjs:assembler');
@@ -2,13 +2,45 @@
2
2
  import type tsStatic from 'typescript';
3
3
  import { type Logger } from '@poppinss/cliui';
4
4
  import type { DevServerOptions } from './types.js';
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.
8
+ *
9
+ * The Dev server performs the following actions
10
+ *
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
+ */
5
16
  export declare class DevServer {
6
17
  #private;
7
18
  constructor(cwd: URL, options: DevServerOptions);
19
+ /**
20
+ * Set a custom CLI UI logger
21
+ */
8
22
  setLogger(logger: Logger): this;
23
+ /**
24
+ * Add listener to get notified when dev server is
25
+ * closed
26
+ */
9
27
  onClose(callback: (exitCode: number) => any): this;
28
+ /**
29
+ * Add listener to get notified when dev server exists
30
+ * with an error
31
+ */
10
32
  onError(callback: (error: any) => any): this;
33
+ /**
34
+ * Close watchers and running child processes
35
+ */
36
+ close(): Promise<void>;
37
+ /**
38
+ * Start the development server
39
+ */
11
40
  start(): Promise<void>;
41
+ /**
42
+ * Start the development server in watch mode
43
+ */
12
44
  startAndWatch(ts: typeof tsStatic, options?: {
13
45
  poll: boolean;
14
46
  }): Promise<void>;