Create `start` and `dev` Commands with Improved Implementation & Move to `tsx`
Introduction
The existing CLI commands for starting the application and running in development mode could be simplified and optimized. Currently, the following commands are used:
-
Start:
node -r dotenv/config -r ./dist/register-path.js ./dist/src/main.js -
Development:
tsnd -r dotenv/config -r tsconfig-paths/register ./src/main.ts
The proposal is to replace these with two simple commands, start and dev, and change the underlying package from ts-node-dev to tsx.
Proposed Changes
-
Replace Start Command: Introduce a
startcommand to replace the current complex command. -
Replace Development Command: Introduce a
devcommand for development, replacing the existing command. - Move from ts-node-dev to tsx: As ts-node-dev has not been actively maintained and tsx (https://github.com/esbuild-kit/tsx) has been more broadly used, it is suggested to migrate to tsx, which may provide better performance and compatibility.
Implementation Details
start Command:
The start command can replace the current command for running the application.
expressots start
dev Command:
The dev command can replace the current command for running the development server.
expressots dev
Use tsx:
Move from ts-node-dev to tsx
Overview
I'm proposing to migrate from ts-node-dev to tsx (https://github.com/esbuild-kit/tsx) for running TypeScript files. This change is driven by the following critical factors:
Active Maintenance
- ts-node-dev: The ts-node-dev package has not been actively maintained, with the latest updates showing a lack of consistent development. This poses a risk of running into unsupported issues or bugs that might not be addressed promptly.
- tsx: In contrast, tsx is an actively maintained package with regular updates, ensuring that it keeps up with the latest TypeScript features and best practices. This active maintenance promotes stability and support for future enhancements.
Performance
- Faster Compilation: tsx uses esbuild, known for its remarkable compilation speed. This speed can make the development process more efficient, particularly in large codebases.
- Reduced Startup Time: tsx optimizes the startup time, enabling quicker iterations during development. This can save valuable time for developers and streamline the workflow.
Compatibility
- Latest TypeScript Features: tsx supports the latest TypeScript features, ensuring compatibility with modern development practices and tools.
- Flexibility with Configuration: tsx provides a flexible configuration system, allowing customization and fine-tuning to fit the specific needs of the project.
Community Adoption
- Wider Usage: tsx has seen more extensive adoption within the community, reflecting a positive reception and trust in the package.
- Contributions: Being a more popular package, tsx benefits from community contributions, resulting in a more refined and robust solution.
Conclusion
The migration from ts-node-dev to tsx offers substantial benefits, including enhanced performance, active maintenance, and compatibility with modern development practices. Aligning with a package that enjoys broader community support will contribute to the project's long-term success and maintainability. Therefore, transitioning to tsx is a strategic move that we believe will positively impact the development experience in ExpressoTS.
Benefits
- Simplicity: Simplifies the commands to start and develop the project.
- Maintainability: Utilizes a well-maintained and widely-used package (
tsx) over ts-node-dev. - Performance: Potentially provides improved speed with tsx.
Conclusion
These changes aim to enhance the CLI experience by providing more straightforward commands and migrating to a more efficient and actively maintained package. Feedback are welcome for this proposed update.
If you are referring to the commands in the templates, the correct commands are these ones:
"build": "tsc -p tsconfig.build.json && cp -R ./register-path.js tsconfig.build.json package.json ./dist",
"dev": "tsnd -r dotenv/config -r tsconfig-paths/register ./src/main.ts",
"prod": "node -r dotenv/config -r ./dist/register-path.js ./dist/src/main.js",
These commands above are project bind commands, they are not available globally from the expressots/cli
Are you suggesting to add these commands to the CLI and then change the signature of the project command of the templates to possibly:
expressots build
expressots dev
expressots prod
Is this the correct understanding?
If this is what you are trying to achieve, I think is a good idea however you need to have in mind that we gonna be generating a dependency from the template to the CLI. And any change in the template that involves commands will have to change CLI as well.
Please, create a POC to demonstrate this proposal to understand the impact, the dependencies that will be included, dependency sizes, as well as a comparison between tsnd versus tsx performance during development. Attach your code and send a link to your POC to be appreciated.
@rsaz I've been attempting to integrate tsx into the project without success. It appears the issue may be related to the Env validator. Should we simplify our approach by focusing on the start and dev commands, moving the tsnd dependency to the CLI?
@Daniel-Boll can you respond please to the questions above mentioned in the previous message? This will help us to understand how do you intend to implement and resolve those issues mentioned above.
Yes, that's the plan. Instead of using tsnd -r dotenv/config -r tsconfig-paths/register ./src/main.ts, we'll simplify it to just expressots dev for instance.
For now, I'd say we stick with tsnd, and initially only port the start and dev commands. Essentially, I believe we're just moving the dependency from the core to the CLI this way.
Ok, in doing that, a simple command will depend on cli change. Ok, lets do a POC, please proceed with a POC. I will move this idea to stage 2
@joaoneto as mentioned we are in the stage 2. Let us know, or discuss with @Daniel-Boll so that you guys can POC these two scenarios:
1 - Change the commands from
"build": "tsc -p tsconfig.build.json && cp -R ./register-path.js tsconfig.build.json package.json ./dist",
"dev": "tsnd -r dotenv/config -r tsconfig-paths/register ./src/main.ts",
"prod": "node -r dotenv/config -r ./dist/register-path.js ./dist/src/main.js",
To
expressots build
expressots dev
expressots prod
2 - Your proposal #16
The changes that I propose are the following:
Add a command option, for example: --experimental, --use-swc, so that the template uses the build with SWC Change or create a template so that the npm scripts of the package.json use the experimental build
Waiting feedback!
Nice!
A suggestion to proceed with the two changes, would be to create in the cli the bin scripts of the package.json, that will trigger:
expressots build
expressots dev
expressots prod
once we opt to use the swc transpiler, we would pass a flag:
expressots build --experimental
expressots dev --experimental
expressots prod --experimental
I created a basic funcional project that uses expressots with the build/test/etc.. tools in the swc's performant environment called expressots-opinionated-experimental so that we can have some insights too, what do you think?
@joaoneto, the progress appears promising, and your approach seems solid. The next logical step might be to conduct a benchmark, comparing the performance with and without the integration of SWC in the build process.
It would be beneficial to document these findings in the README file itself, providing clear insight for anyone exploring the repository.
Rest assured, I'll be monitoring the changes in the repo by subscribing to the changes look forward to seeing the results.
Best Regards, Daniel Boll. :flower_playing_cards:
@joaoneto to your points:
Should we separate bin expressots scripts?
- I would advise to not at least now, the reason is that more packages to maintain, add ci/cd, format, document and so on
- I think we can have the same script just with an additional flag
experimentalas you suggested
Should we create templates for experimental?
- yes absolutely. We could organize like this:
- tsconfig
- opinionated
- non opinionated
- swc
- opinionated
- non opinionated
Few things to consider:
- If the user decide to use swc we need to test all current functionalities available in tsconfig, they should work on swc as well
- We need to test on win and wsl
- Type Checking: SWC itself does not perform TypeScript type checking. It just cares about transforming code as quickly as possible. We rely on TypeScript's type-checking capabilities so we will still need to run the TypeScript compiler (tsc --noEmit) to validate your types.
Configuration: SWC has its own configuration file (.swcrc), which is separate from TypeScript's tsconfig.json. You will have to configure SWC separately to ensure that it understands your project's needs. Now we gonna have 2 configuration files, we need to clearly define where the user should use one rather than the other, or for swc just use .swcrc file
Compatibility: SWC aims to support most TypeScript syntax and features, but there might be subtle differences or unsupported edge cases. It's a good idea to thoroughly test the templates/application
Yeah, lets proceed and observe these points. Once you get a branch send me out to test as well.
Keep up the good work!
@joaoneto added your comments from #16 to here
Let me know about the points above. Also, I will be testing end of the day to provide a proper technical feedback.
@joaoneto to your points:
Should we separate bin expressots scripts?
- I would advise to not at least now, the reason is that more packages to maintain, add ci/cd, format, document and so on
- I think we can have the same script just with an additional flag
experimentalas you suggestedShould we create templates for experimental?
yes absolutely. We could organize like this:
tsconfig
- opinionated
- non opinionated
swc
- opinionated
- non opinionated
Few things to consider:
- If the user decide to use swc we need to test all current functionalities available in tsconfig, they should work on swc as well
- We need to test on win and wsl
- Type Checking: SWC itself does not perform TypeScript type checking. It just cares about transforming code as quickly as possible. We rely on TypeScript's type-checking capabilities so we will still need to run the TypeScript compiler (tsc --noEmit) to validate your types.
Configuration: SWC has its own configuration file (.swcrc), which is separate from TypeScript's tsconfig.json. You will have to configure SWC separately to ensure that it understands your project's needs. Now we gonna have 2 configuration files, we need to clearly define where the user should use one rather than the other, or for swc just use .swcrc file
Compatibility: SWC aims to support most TypeScript syntax and features, but there might be subtle differences or unsupported edge cases. It's a good idea to thoroughly test the templates/application
Yeah, lets proceed and observe these points. Once you get a branch send me out to test as well.
Keep up the good work!
@rsaz Very well observed!
Let me see if I understand. I imagine that for the script bin build and dev, two concurrent tasks, tsc --noEmit (–watch for dev) and the swc task that only transpiles, we can add the lib concurrently to run on any platform, or create our own solution that uses spawn to start both tasks in parallel.
Unfortunately, swc currently does not support tsconfig.json, so .swcrc would have to be kept only for transpiling. It doesn’t need to change, and tsconfig.json would be the source of truth, that validate types with (tsc --noEmit).
Those who opt to use the experimental template should accept the risks of possibly not being compatible with some extreme case without swc support. We should have a very clear warning that this is a tradeoff between optimizing build time and the features you intend to use.
Solution moved to approved. I think now we have covered the major scenarios Now you can use #16 to implement it. Make sure on #16 you refer the discussion on #15
Looking forward to review your submission
Currently the CLI already supports the commands inferred:
"build": "expressots run build",
"dev": "expressots run dev",
"prod": "expressots run prod",
I will create a ticket to compress even more to:
"build": "expressots build",
"dev": "expressots dev",
"prod": "expressots prod",
Also allow parameters to be passed as well as to change the ts-node-dev dependency out of the picture,
Alternatives to be compared:
- nodemon --watch + ts-node
- node-dev + ts-node
- ts-node-dev -tsx (https://www.npmjs.com/package/tsx)
- nodemon + node-dev + ts-node + swc
- swc
At the time I tried tsx, but couldn't make it work for some reason, tried for a while but unsuccessful, I then quit trying. Albeit being one hell of a package.
Done in the v3.0.0