Replies: 3 comments 11 replies
-
As we are about to support HTML entry, will inline stylesheets and scripts be considered as dependencies? And how to represent them? <style>
/* some styles */
@import url('./src/some-style.css');
</style>
<script>
// some script
</script>
<script type="module">
// some module script
import * as script from './src/some-script.js'
</script>
<script type="x-shader/x-fragment">
// some shader
</script> |
Beta Was this translation helpful? Give feedback.
-
Dependency abstraction is a good solution to the problem that the same module behaves differently in different ways of reference. What I'm more worried about is that Dependency abstraction make module relationship changing from |
Beta Was this translation helpful? Give feedback.
-
Do we need |
Beta Was this translation helpful? Give feedback.
-
THIS RFC is withdrawn in favor of the WIP dependency RFC.
Summary
This RFC proposes adding a dependency abstraction. Adding this abstraction will help rspack to add these functionalities:
Motivation
For the status quo, after the asset support, rspack can handle the asset emitting and generate a javascript-based module like this:
Importing asset from JavaScript-like modules
which generates,
Support for CSS
Given an example for CSS, you can found the solution above can only resolve part of the problem.
Since an asset can be formatted as
AssetResource
/AssetInline
, etc. The canonicalized url function might be changed base on its max-size settings, which means the content of the url might be replaced. The reason why this wouldn't affect JavaScript-like modules is that we wrap JavaScript modules into runtimes which can return a new url or inlined based64, however, we cannot take the advantage of this for CSS, thus, Dependency comes for solving this kind of problems:Affected examples
Here are some additional examples that require dependency abstraction.
CSSUrlDependency
URL should be pointed to the asset location or inlined.
CSSImportDependency
CSS imports with media queries / supports query
External resources are intended to keep as is, and the local resources are supposed to be wrapped in media queries.
HTML scripts and styles
In the case above, href in link tags and source in script tags should be treated as dependencies and these references to the asset location should be replaced.
Experimental: Import Assertions
Proposal
Import Assertions will change the module type to asserted type.
Detailed design
trait
Dependency
Basically, a
Dependency
contains these stuff:const KIND
:KIND
is an identifier for debuggingfn specifier
: a specifier targeting the importing module, queries are included.fn resolve_kind
: the resolve kind indicates the type of the reference, such asdynamicImport
/Import
/AtImport
, etc.fn assertion_type
: the assertion type, which returnsNone
by default. If an assertion is set, a correspondingAssertionType
is supposed to be returned.Since a dependency implements trait
Hash
, We have to provide an ID generator to create a normalized and unified dependency id.Dependency collecting and rendering
Asset handling
This part is for those modules that do not have the corresponding runtime and help those to point assets to the generated location, like CSS. JavaScript(with custom runtime) is handled by the commonjs runtime, so this feature is not a must for them.
Rspack collects dependency in each module implementation. Each module is supposed to implement
fn dependencies
with a boxed struct that implementstrait Dependency
returned.In the meantime, you can utilize this function(with
&mut self
), to create a hash replacement for each module, such as in the CSS module.The returned dependencies will be used to create
ModuleGraphModule
in the module graph of rspack.After the
render_manifest
, the URLs in CSS Modules are replaced with hashes and we collect all the code generation results as assets, so we can directly replace the hash with the real generated locations of the assets.Imports that affect the module rendering
In those files like CSS, the at-import statement might affect how an importee is included in the bundle. Let's grab the example above.
The code generation result will be as follows:
The core idea to implement this in Rspack is to give the dependency in each chunk to the corresponding module render function(you may find this on
trait Module
) just like this.In the CSS world, at-import with the same specifier could be called with different media queries, which means we have to iterate through the incoming dependency list, and if which is found, we need to do the fallback just as follows:
Detail about CSS support is out of range of this RFC.
In conclusion, we have to support
get_incoming_dependency_by_module_uri
for the module graph and let the importee handle the dependency rendering logic.Overriding module type based on import assertions
Since each dependency contains
assertion_type
, and a new module factory is created with a dependency, so we can directly set the context containingmodule_type
fromassertion_type
.Module Identifier
A module identifier in rspack is used as a key to determine whether a module should be reanalyzed or not. Currently, we use a
visited_module_identity: HashSet<String>
with module URI to avoid duplicated analysis. However, the identifiers used to avoid duplicated behavior in the module creation step and the module render step should be different.Firstly, I would like to use the import assertion example to describe this, and use module URI as the key:
In this case, as the URI(specifier) are the same, so rspack will not create a new module for the second line, however, we need to regard the module type of the secondly imported module as
javascript
. Using the URI as the key stops us from creating a new module. It's a bummer.If we use dependency id as the key, and since dependency id is generated with assertions taken into account, rspack will generate two modules. Great!
Here's another example. Again, let me use the CSS example to describe this, and at this time, we use dependency id as the key:
In the example above, the CSS file
landscape.css
is included in multiple files:index.css
andfoo.css
, and in the world of CSS, an at-import with or without the media query does not change the module data like type, and the id of a dependency is not generated based on media query by default. So again, the problem is solved! If later the CSS specification add a new specification, we can still define our customfn id
for a CSS dependency.In conclusion of the example above, we have to change the key to the id of each dependency.
But what about the module id? Using the dependency id as the module id is ok, but it's not enough. Again, with the CSS example above, we need to find the incoming dependency to a certain specifier not the key used in the
HashSet
.Alternatives
You may refer to the prior art section.
Drawbacks
When I first started to design the dependency abstraction, I would like to provide an
render
method for each dependency. For example, CSS need to wrap media queries around the module render result. But each dependency's rendering logic is different and we don't know the required information for each module type. Webpack's dependency template is only used to patch the asset location. The workaround is to use the downcast for each dependency, and it's ok to do that.Prior art
How Webpack works?
Glossary
WebpackAST
, which is forced to be a JavaScript AST, other ASTs like CSS AST is not supported, and webpack can decide whether it should reuse AST.range
. It is also worth to notice that dependency included in different module types are different, for example:CSSUrlDependency
/CSSImportDependency
/HarmonyImportDependency
.HarmonyImport
s would be replaced to webpack requires (see:getImportStatement
inHarmonyImportDependency
)Example
Here's a snippet of CSS:
which generates two dependencies as follows:
Take a look at the
CSSUrlDependency
, the URL of which might be replaced with an updated location point to theassetModuleFilename
or inline-base64 determined by options. Thus, webpack provides an abstraction on the replacement, which is called a dependency template:Architecture conflicts between webpack and rspack
Before going deep into the actual conflict, I would like to dig more into how webpack works from the perspective of ASTs and templates. We assume the module type is
javascript/auto
for the example below.Firstly, webpack will read all resources as a UTF-8 string by default. Once a loader is encountered, you can transform the code into an
ESTree, then you may invoke the loader callback with the code or
webpackAST
(passed as a property for the fourth parameter). If the AST is returned, the parser will directly analyze this AST, or webpack will parse the code return invoked with the callback and continue the analysis procedure.Secondly, after all the analysis is done, webpack applies templates that were registered with code presented as
module.originalSource()
and patches the code, then emit the assets.In conclusion, webpack basically handles string values everywhere. Like how webpack does, most bundlers collect dependency after the transform. However, Rspack handles
AST
directly and generates code based onAST
s, which basically means if we collect dependencies withrange
we cannot easily replace the string created withrange
by the dependency template since the code generation results may be different. Another approach for this is to do the code generation after each transformation, however this approach is not decent as code generation logic should not be included in the build step.Conclusion
Webpack bundle system is built on string patches. The abstraction of
range
is not quite suitable with the rspack architecture, and an AST-friendly approach is necessary.How parcel works?
Rspack's transformer architecture is more likely to parcel's. In parcel's transformer architecture, a transformer may add dependencies manually just like this:
addURLDependency
creates a dependency bounded to the asset internally and returns the placeholder. The hash is calculated based-on the follows. You may refer to this to check out the real implementation.After the code generation, parcel replaces the placeholder with real generated paths, link
Unresolved questions
Beta Was this translation helpful? Give feedback.
All reactions