In the "Basic Usage" section, we already knew that you can modify the output files of a project through the buildConfig
configuration. buildConfig
not only describes some of the features of the product, but also provides some functionality for building the product.
If you are not familiar with buildConfig
, please read modify-output-product.
In this chapter we'll dive into the use of certain build configurations and understand what happens when the modern build
command is executed.
bundle
/ bundleless
So first let's understand bundle and bundleless.
A bundle is a package of build artifacts, which may be a single file or multiple files based on a certain code splitting strategy.
bundleless, on the other hand, means that each source file is compiled and built separately, but not bundled together. Each output file can be found with its corresponding source code file. The process of bundleless build can also be understood as the process of code conversion of source files only.
They have their own benefits.
bundleless is a single-file compilation mode, so for referencing and exporting types, you need to add the type
keyword. For example, import type { A } from './types'
. Please refer to the esbuild documentation for more information.
In buildConfig
you can specify whether the current build task is bundle or bundleless by using buildConfig.buildType
.
input
/ sourceDir
buildConfig.input
is used to specify the path to a file or directory from which to read the source code, the default value of which varies between bundle and bundleless builds:
buildType: 'bundle'
, input
defaults to src/index.(j|t)sx?
.buildType: 'bundleless'
, input
defaults to ['src']
.From the default value, we know that building in bundle mode usually specifies one or more files as the entry point for the build, while building in bundleless mode specifies a directory and uses all the files in that directory as the entry point.
sourceDir
is used to specify the source directory, which is only related to the following two elements:
outbase
for specifying the build processSo we can get its best practices:
input
during the bundle build.sourceDir
(where input
will be aligned with sourceDir
). If we want to use the input
in bundleless, we only need to specify sourceDir
.If you want to convert only some of the files in bundleless, e.g. only the files in the src/runtime
directory, you need to configure input
:
In some scenarios, esbuild is not enough to meet our needs, and we will use swc to do the code conversion.
Starting from version 2.36.0, the Modern.js Module will use swc by default when it comes to the following functionality, but that doesn't mean we don't use esbuild any more, the rest of the functionality will still use esbuild.
In fact, we've been using swc for full code conversion since version 2.16.0. However, swc also has some limitations, so we added sourceType to turn off swc when the source is formatted as 'commonjs', which isn't really user-intuitive, and the cjs mode of the swc formatted outputs don't have annotate each export name, which can cause problems in node. So we deprecated this behaviour and went back to the original design - using swc as a supplement only in situations where it was needed.
The Modern.js Module provides a Hook mechanism that allows us to inject custom logic at different stages of the build process. The Modern.js Module Hook is implemented using tapable, which extends esbuild's plugin mechanism, and is recommended to be used directly if esbuild plugins already meet your needs. Here's how to use it:
Serial hooks that stop the execution of other tapped functions if a tapped function returns a non-undefined result.
Serial hooks whose results are passed to the next tapped function.
The execution order of hooks follows the registration order. You can control whether a hook is registered before or after the built-in hooks using applyAfterBuiltIn
.
The buildConfig.dts
configuration is mainly used for type file generation.
Type generation is turned on by default, if you need to turn it off, you can configure it as follows:
The build speed is generally improved by closing the type file.
With buildType: 'bundleless'
, type files are generated using the project's tsc
command to complete production.
The Modern.js Module also supports bundling of type files, although care needs to be taken when using this feature.
buildConfig.externals
or close dts.respectExternal to external all third-party packages types..ts
file. For example, the package.json
of a third-party dependency contains something like this: {"types": ". /src/index.ts"}
.For the above problems, our recommended approach is to first use tsc
to generate d.ts files, then package the index.d.ts as the entry and close dts.respectExternal
. In the future evolution, we will gradually move towards this handling approach.
During the bundleless build process, if an alias appears in the source code, e.g.
The type files generated with tsc
will also contain these aliases. However, Modern.js Module will convert the aliases in the type file generated by tsc
.
dts
General usage:
For the use of dts.only
:
When the modern build
command is executed, the
buildConfig.outDir
.js/ts
source code to generate the JS build artifacts for bundle/bundleless.tsc
.When a build error occurs, based on the information learned above, it is easy to understand what error appears in the terminal.
Errors reported for js or ts builds:
Errors reported for the type file generation process:
For js/ts
build errors, we can tell from the error message.
'bundle failed:'
to determine if the error is reported for a bundle build or a bundleless buildformat
of the build processtarget
of the build processFrom 2.36.0, For troubleshooting purposes, the Modern.js Module provides a debug mode, which you can enable by adding the DEBUG=module environment variable when executing a build.
In debug mode, you'll see more detailed build logs output in Shell, which are mainly process logs:
In addition, Module provides the ability to debug internal workflows. You can enable more detailed debugging logging by setting the DEBUG=module:*
environment variable.
Currently, only DEBUG=module:resolve
is supported, which allows you to see a detailed log of module resolution within the Module.