@adonisjs/assembler 6.1.3-9 → 7.0.0-0

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.
@@ -1,158 +0,0 @@
1
- /*
2
- * @adonisjs/core
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 { cliui } from '@poppinss/cliui';
10
- import { run } from './helpers.js';
11
- /**
12
- * Instance of CLIUI
13
- */
14
- const ui = cliui();
15
- /**
16
- * Exposes the API to start the development server for processing assets during
17
- * development.
18
- *
19
- * - Here we are running the assets dev server in a child process.
20
- * - Piping the output from the child process and reformatting it before writing it to
21
- * process streams.
22
- *
23
- * AssetsDevServer is agnostic and can run any assets dev server. Be it Vite or Encore or
24
- * even Webpack directly.
25
- */
26
- export class AssetsDevServer {
27
- #cwd;
28
- #logger = ui.logger;
29
- #options;
30
- #devServer;
31
- /**
32
- * Getting reference to colors library from logger
33
- */
34
- get #colors() {
35
- return this.#logger.getColors();
36
- }
37
- constructor(cwd, options) {
38
- this.#cwd = cwd;
39
- this.#options = options;
40
- }
41
- /**
42
- * Logs messages from vite dev server stdout and stderr
43
- */
44
- #logViteDevServerMessage(data) {
45
- const dataString = data.toString();
46
- const lines = dataString.split('\n');
47
- /**
48
- * Logging VITE ready in message with proper
49
- * spaces and newlines
50
- */
51
- if (dataString.includes('ready in')) {
52
- console.log('');
53
- console.log(dataString.trim());
54
- return;
55
- }
56
- /**
57
- * Put a wrapper around vite network address log
58
- */
59
- if (dataString.includes('Local') && dataString.includes('Network')) {
60
- const sticker = ui.sticker().useColors(this.#colors).useRenderer(this.#logger.getRenderer());
61
- lines.forEach((line) => {
62
- if (line.trim()) {
63
- sticker.add(line);
64
- }
65
- });
66
- sticker.render();
67
- return;
68
- }
69
- /**
70
- * Log rest of the lines
71
- */
72
- lines.forEach((line) => {
73
- if (line.trim()) {
74
- console.log(line);
75
- }
76
- });
77
- }
78
- /**
79
- * Logs messages from assets dev server stdout and stderr
80
- */
81
- #logAssetsDevServerMessage(data) {
82
- const dataString = data.toString();
83
- const lines = dataString.split('\n');
84
- lines.forEach((line) => {
85
- if (line.trim()) {
86
- console.log(line);
87
- }
88
- });
89
- }
90
- /**
91
- * Set a custom CLI UI logger
92
- */
93
- setLogger(logger) {
94
- this.#logger = logger;
95
- return this;
96
- }
97
- /**
98
- * Starts the assets bundler server. The assets bundler server process is
99
- * considered as the secondary process and therefore we do not perform
100
- * any cleanup if it dies.
101
- */
102
- start() {
103
- if (!this.#options?.serve) {
104
- return;
105
- }
106
- this.#logger.info(`starting "${this.#options.driver}" dev server...`);
107
- /**
108
- * Create child process
109
- */
110
- this.#devServer = run(this.#cwd, {
111
- script: this.#options.cmd,
112
- /**
113
- * We do not inherit the stdio for vite and encore, because in
114
- * inherit mode they own the stdin and interrupts the
115
- * `Ctrl + C` command.
116
- */
117
- stdio: 'pipe',
118
- scriptArgs: this.#options.args,
119
- });
120
- /**
121
- * Log child process messages
122
- */
123
- this.#devServer.stdout?.on('data', (data) => {
124
- if (this.#options.driver === 'vite') {
125
- this.#logViteDevServerMessage(data);
126
- }
127
- else {
128
- this.#logAssetsDevServerMessage(data);
129
- }
130
- });
131
- this.#devServer.stderr?.on('data', (data) => {
132
- if (this.#options.driver === 'vite') {
133
- this.#logViteDevServerMessage(data);
134
- }
135
- else {
136
- this.#logAssetsDevServerMessage(data);
137
- }
138
- });
139
- this.#devServer
140
- .then((result) => {
141
- this.#logger.warning(`"${this.#options.driver}" dev server closed with status code "${result.exitCode}"`);
142
- })
143
- .catch((error) => {
144
- this.#logger.warning(`unable to connect to "${this.#options.driver}" dev server`);
145
- this.#logger.fatal(error);
146
- });
147
- }
148
- /**
149
- * Stop the dev server
150
- */
151
- stop() {
152
- if (this.#devServer) {
153
- this.#devServer.removeAllListeners();
154
- this.#devServer.kill('SIGKILL');
155
- this.#devServer = undefined;
156
- }
157
- }
158
- }
@@ -1,223 +0,0 @@
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 slash from 'slash';
10
- import copyfiles from 'cpy';
11
- import fs from 'node:fs/promises';
12
- import { fileURLToPath } from 'node:url';
13
- import { join, relative } from 'node:path';
14
- import { cliui } from '@poppinss/cliui';
15
- import { run, parseConfig } from './helpers.js';
16
- /**
17
- * Instance of CLIUI
18
- */
19
- const ui = cliui();
20
- /**
21
- * The bundler class exposes the API to build an AdonisJS project.
22
- */
23
- export class Bundler {
24
- #cwd;
25
- #cwdPath;
26
- #ts;
27
- #logger = ui.logger;
28
- #options;
29
- /**
30
- * Getting reference to colors library from logger
31
- */
32
- get #colors() {
33
- return this.#logger.getColors();
34
- }
35
- constructor(cwd, ts, options) {
36
- this.#cwd = cwd;
37
- this.#cwdPath = fileURLToPath(this.#cwd);
38
- this.#ts = ts;
39
- this.#options = options;
40
- }
41
- #getRelativeName(filePath) {
42
- return slash(relative(this.#cwdPath, filePath));
43
- }
44
- /**
45
- * Cleans up the build directory
46
- */
47
- async #cleanupBuildDirectory(outDir) {
48
- await fs.rm(outDir, { recursive: true, force: true, maxRetries: 5 });
49
- }
50
- /**
51
- * Runs assets bundler command to build assets.
52
- */
53
- async #buildAssets() {
54
- const assetsBundler = this.#options.assets;
55
- if (!assetsBundler?.serve) {
56
- return true;
57
- }
58
- try {
59
- this.#logger.info('compiling frontend assets', { suffix: assetsBundler.cmd });
60
- await run(this.#cwd, {
61
- stdio: 'inherit',
62
- script: assetsBundler.cmd,
63
- scriptArgs: [],
64
- });
65
- return true;
66
- }
67
- catch {
68
- return false;
69
- }
70
- }
71
- /**
72
- * Runs tsc command to build the source.
73
- */
74
- async #runTsc(outDir) {
75
- try {
76
- await run(this.#cwd, {
77
- stdio: 'inherit',
78
- script: 'tsc',
79
- scriptArgs: ['--outDir', outDir],
80
- });
81
- return true;
82
- }
83
- catch {
84
- return false;
85
- }
86
- }
87
- /**
88
- * Copy files to destination directory
89
- */
90
- async #copyFiles(files, outDir) {
91
- try {
92
- await copyfiles(files, outDir, { parents: true, cwd: this.#cwdPath });
93
- }
94
- catch (error) {
95
- if (!error.message.includes("the file doesn't exist")) {
96
- throw error;
97
- }
98
- }
99
- }
100
- /**
101
- * Copy meta files to the output directory
102
- */
103
- async #copyMetaFiles(outDir, additionalFilesToCopy) {
104
- const metaFiles = (this.#options.metaFiles || [])
105
- .map((file) => file.pattern)
106
- .concat(additionalFilesToCopy);
107
- await this.#copyFiles(metaFiles, outDir);
108
- }
109
- /**
110
- * Copies .adonisrc.json file to the destination
111
- */
112
- async #copyAdonisRcFile(outDir) {
113
- const existingContents = JSON.parse(await fs.readFile(join(this.#cwdPath, '.adonisrc.json'), 'utf-8'));
114
- const compiledContents = Object.assign({}, existingContents, {
115
- typescript: false,
116
- lastCompiledAt: new Date().toISOString(),
117
- });
118
- await fs.mkdir(outDir, { recursive: true });
119
- await fs.writeFile(join(outDir, '.adonisrc.json'), JSON.stringify(compiledContents, null, 2) + '\n');
120
- }
121
- /**
122
- * Returns the lock file name for a given packages client
123
- */
124
- #getClientLockFile(client) {
125
- switch (client) {
126
- case 'npm':
127
- return 'package-lock.json';
128
- case 'yarn':
129
- return 'yarn.lock';
130
- case 'pnpm':
131
- return 'pnpm-lock.yaml';
132
- }
133
- }
134
- /**
135
- * Returns the installation command for a given packages client
136
- */
137
- #getClientInstallCommand(client) {
138
- switch (client) {
139
- case 'npm':
140
- return 'npm ci --omit="dev"';
141
- case 'yarn':
142
- return 'yarn install --production';
143
- case 'pnpm':
144
- return 'pnpm i --prod';
145
- }
146
- }
147
- /**
148
- * Set a custom CLI UI logger
149
- */
150
- setLogger(logger) {
151
- this.#logger = logger;
152
- return this;
153
- }
154
- /**
155
- * Bundles the application to be run in production
156
- */
157
- async bundle(stopOnError = true, client = 'npm') {
158
- /**
159
- * Step 1: Parse config file to get the build output directory
160
- */
161
- const config = parseConfig(this.#cwd, this.#ts);
162
- if (!config) {
163
- return false;
164
- }
165
- /**
166
- * Step 2: Cleanup existing build directory (if any)
167
- */
168
- const outDir = config.options.outDir || fileURLToPath(new URL('build/', this.#cwd));
169
- this.#logger.info('cleaning up output directory', { suffix: this.#getRelativeName(outDir) });
170
- await this.#cleanupBuildDirectory(outDir);
171
- /**
172
- * Step 3: Build frontend assets
173
- */
174
- if (!(await this.#buildAssets())) {
175
- return false;
176
- }
177
- /**
178
- * Step 4: Build typescript source code
179
- */
180
- this.#logger.info('compiling typescript source', { suffix: 'tsc' });
181
- const buildCompleted = await this.#runTsc(outDir);
182
- await this.#copyFiles(['ace.js'], outDir);
183
- /**
184
- * Remove incomplete build directory when tsc build
185
- * failed and stopOnError is set to true.
186
- */
187
- if (!buildCompleted && stopOnError) {
188
- await this.#cleanupBuildDirectory(outDir);
189
- const instructions = ui
190
- .sticker()
191
- .fullScreen()
192
- .drawBorder((borderChar, colors) => colors.red(borderChar));
193
- instructions.add(this.#colors.red('Cannot complete the build process as there are TypeScript errors.'));
194
- instructions.add(this.#colors.red('Use "--ignore-ts-errors" flag to ignore TypeScript errors and continue the build.'));
195
- this.#logger.logError(instructions.prepare());
196
- return false;
197
- }
198
- /**
199
- * Step 5: Copy meta files to the build directory
200
- */
201
- const pkgFiles = ['package.json', this.#getClientLockFile(client)];
202
- this.#logger.info('copying meta files to the output directory');
203
- await this.#copyMetaFiles(outDir, pkgFiles);
204
- /**
205
- * Step 6: Copy .adonisrc.json file to the build directory
206
- */
207
- this.#logger.info('copying .adonisrc.json file to the output directory');
208
- await this.#copyAdonisRcFile(outDir);
209
- this.#logger.success('build completed');
210
- this.#logger.log('');
211
- /**
212
- * Next steps
213
- */
214
- ui.instructions()
215
- .useRenderer(this.#logger.getRenderer())
216
- .heading('Run the following commands to start the server in production')
217
- .add(this.#colors.cyan(`cd ${this.#getRelativeName(outDir)}`))
218
- .add(this.#colors.cyan(this.#getClientInstallCommand(client)))
219
- .add(this.#colors.cyan('node bin/server.js'))
220
- .render();
221
- return true;
222
- }
223
- }
@@ -1,253 +0,0 @@
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 picomatch from 'picomatch';
10
- import { cliui } from '@poppinss/cliui';
11
- import { AssetsDevServer } from './assets_dev_server.js';
12
- import { getPort, isDotEnvFile, isRcFile, runNode, watch } from './helpers.js';
13
- /**
14
- * Instance of CLIUI
15
- */
16
- const ui = cliui();
17
- /**
18
- * Exposes the API to start the development. Optionally, the watch API can be
19
- * used to watch for file changes and restart the development server.
20
- *
21
- * The Dev server performs the following actions
22
- *
23
- * - Assigns a random PORT, when PORT inside .env file is in use
24
- * - Uses tsconfig.json file to collect a list of files to watch.
25
- * - Uses metaFiles from .adonisrc.json file to collect a list of files to watch.
26
- * - Restart HTTP server on every file change.
27
- */
28
- export class DevServer {
29
- #cwd;
30
- #logger = ui.logger;
31
- #options;
32
- #isWatching = false;
33
- #scriptFile = 'bin/server.js';
34
- #isMetaFileWithReloadsEnabled;
35
- #isMetaFileWithReloadsDisabled;
36
- #onError;
37
- #onClose;
38
- #httpServer;
39
- #watcher;
40
- #assetsServer;
41
- /**
42
- * Getting reference to colors library from logger
43
- */
44
- get #colors() {
45
- return this.#logger.getColors();
46
- }
47
- constructor(cwd, options) {
48
- this.#cwd = cwd;
49
- this.#options = options;
50
- this.#isMetaFileWithReloadsEnabled = picomatch((this.#options.metaFiles || [])
51
- .filter(({ reloadServer }) => reloadServer === true)
52
- .map(({ pattern }) => pattern));
53
- this.#isMetaFileWithReloadsDisabled = picomatch((this.#options.metaFiles || [])
54
- .filter(({ reloadServer }) => reloadServer !== true)
55
- .map(({ pattern }) => pattern));
56
- }
57
- /**
58
- * Inspect if child process message is from AdonisJS HTTP server
59
- */
60
- #isAdonisJSReadyMessage(message) {
61
- return (message !== null &&
62
- typeof message === 'object' &&
63
- 'isAdonisJS' in message &&
64
- 'environment' in message &&
65
- message.environment === 'web');
66
- }
67
- /**
68
- * Conditionally clear the terminal screen
69
- */
70
- #clearScreen() {
71
- if (this.#options.clearScreen) {
72
- process.stdout.write('\u001Bc');
73
- }
74
- }
75
- /**
76
- * Starts the HTTP server
77
- */
78
- #startHTTPServer(port, mode) {
79
- this.#httpServer = runNode(this.#cwd, {
80
- script: this.#scriptFile,
81
- env: { PORT: port, ...this.#options.env },
82
- nodeArgs: this.#options.nodeArgs,
83
- scriptArgs: this.#options.scriptArgs,
84
- });
85
- this.#httpServer.on('message', (message) => {
86
- if (this.#isAdonisJSReadyMessage(message)) {
87
- ui.sticker()
88
- .useColors(this.#colors)
89
- .useRenderer(this.#logger.getRenderer())
90
- .add(`Server address: ${this.#colors.cyan(`http://${message.host}:${message.port}`)}`)
91
- .add(`File system watcher: ${this.#colors.cyan(`${this.#isWatching ? 'enabled' : 'disabled'}`)}`)
92
- .render();
93
- }
94
- });
95
- this.#httpServer
96
- .then((result) => {
97
- if (mode === 'nonblocking') {
98
- this.#onClose?.(result.exitCode);
99
- this.#watcher?.close();
100
- this.#assetsServer?.stop();
101
- }
102
- })
103
- .catch((error) => {
104
- if (mode === 'nonblocking') {
105
- this.#onError?.(error);
106
- this.#watcher?.close();
107
- this.#assetsServer?.stop();
108
- }
109
- });
110
- }
111
- /**
112
- * Starts the assets server
113
- */
114
- #startAssetsServer() {
115
- this.#assetsServer = new AssetsDevServer(this.#cwd, this.#options.assets);
116
- this.#assetsServer.setLogger(this.#logger);
117
- this.#assetsServer.start();
118
- }
119
- /**
120
- * Restarts the HTTP server
121
- */
122
- #restartHTTPServer(port) {
123
- if (this.#httpServer) {
124
- this.#httpServer.removeAllListeners();
125
- this.#httpServer.kill('SIGKILL');
126
- }
127
- this.#startHTTPServer(port, 'blocking');
128
- }
129
- /**
130
- * Handles a non TypeScript file change
131
- */
132
- #handleFileChange(action, port, relativePath) {
133
- if (isDotEnvFile(relativePath) || isRcFile(relativePath)) {
134
- this.#clearScreen();
135
- this.#logger.log(`${this.#colors.green(action)} ${relativePath}`);
136
- this.#restartHTTPServer(port);
137
- return;
138
- }
139
- if (this.#isMetaFileWithReloadsEnabled(relativePath)) {
140
- this.#clearScreen();
141
- this.#logger.log(`${this.#colors.green(action)} ${relativePath}`);
142
- this.#restartHTTPServer(port);
143
- return;
144
- }
145
- if (this.#isMetaFileWithReloadsDisabled(relativePath)) {
146
- this.#clearScreen();
147
- this.#logger.log(`${this.#colors.green(action)} ${relativePath}`);
148
- }
149
- }
150
- /**
151
- * Handles TypeScript source file change
152
- */
153
- #handleSourceFileChange(action, port, relativePath) {
154
- this.#clearScreen();
155
- this.#logger.log(`${this.#colors.green(action)} ${relativePath}`);
156
- this.#restartHTTPServer(port);
157
- }
158
- /**
159
- * Set a custom CLI UI logger
160
- */
161
- setLogger(logger) {
162
- this.#logger = logger;
163
- this.#assetsServer?.setLogger(logger);
164
- return this;
165
- }
166
- /**
167
- * Add listener to get notified when dev server is
168
- * closed
169
- */
170
- onClose(callback) {
171
- this.#onClose = callback;
172
- return this;
173
- }
174
- /**
175
- * Add listener to get notified when dev server exists
176
- * with an error
177
- */
178
- onError(callback) {
179
- this.#onError = callback;
180
- return this;
181
- }
182
- /**
183
- * Close watchers and running child processes
184
- */
185
- async close() {
186
- await this.#watcher?.close();
187
- this.#assetsServer?.stop();
188
- if (this.#httpServer) {
189
- this.#httpServer.removeAllListeners();
190
- this.#httpServer.kill('SIGKILL');
191
- }
192
- }
193
- /**
194
- * Start the development server
195
- */
196
- async start() {
197
- this.#clearScreen();
198
- this.#logger.info('starting HTTP server...');
199
- this.#startHTTPServer(String(await getPort(this.#cwd)), 'nonblocking');
200
- this.#startAssetsServer();
201
- }
202
- /**
203
- * Start the development server in watch mode
204
- */
205
- async startAndWatch(ts, options) {
206
- const port = String(await getPort(this.#cwd));
207
- this.#isWatching = true;
208
- this.#clearScreen();
209
- this.#logger.info('starting HTTP server...');
210
- this.#startHTTPServer(port, 'blocking');
211
- this.#startAssetsServer();
212
- /**
213
- * Create watcher using tsconfig.json file
214
- */
215
- const output = watch(this.#cwd, ts, options || {});
216
- if (!output) {
217
- this.#onClose?.(1);
218
- return;
219
- }
220
- /**
221
- * Storing reference to watcher, so that we can close it
222
- * when HTTP server exists with error
223
- */
224
- this.#watcher = output.chokidar;
225
- /**
226
- * Notify the watcher is ready
227
- */
228
- output.watcher.on('watcher:ready', () => {
229
- this.#logger.info('watching file system for changes...');
230
- });
231
- /**
232
- * Cleanup when watcher dies
233
- */
234
- output.chokidar.on('error', (error) => {
235
- this.#logger.warning('file system watcher failure');
236
- this.#logger.fatal(error);
237
- this.#onError?.(error);
238
- output.chokidar.close();
239
- });
240
- /**
241
- * Changes in TypeScript source file
242
- */
243
- output.watcher.on('source:add', ({ relativePath }) => this.#handleSourceFileChange('add', port, relativePath));
244
- output.watcher.on('source:change', ({ relativePath }) => this.#handleSourceFileChange('update', port, relativePath));
245
- output.watcher.on('source:unlink', ({ relativePath }) => this.#handleSourceFileChange('delete', port, relativePath));
246
- /**
247
- * Changes in non-TypeScript source files
248
- */
249
- output.watcher.on('add', ({ relativePath }) => this.#handleFileChange('add', port, relativePath));
250
- output.watcher.on('change', ({ relativePath }) => this.#handleFileChange('update', port, relativePath));
251
- output.watcher.on('unlink', ({ relativePath }) => this.#handleFileChange('delete', port, relativePath));
252
- }
253
- }