Skip to content

Commit

Permalink
feat: support compiler.hooks.assetEmitted (#3387)
Browse files Browse the repository at this point in the history
  • Loading branch information
jerrykingxyz authored Jun 1, 2023
1 parent a17d470 commit d6bebb4
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 12 deletions.
7 changes: 7 additions & 0 deletions crates/node_binding/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ export interface JsAsset {
info: JsAssetInfo
}

export interface JsAssetEmittedArgs {
filename: string
outputPath: string
targetPath: string
}

export interface JsAssetInfo {
/** if the asset can be long term cached forever (contains a hash) */
immutable: boolean
Expand Down Expand Up @@ -178,6 +184,7 @@ export interface JsHooks {
compilation: (...args: any[]) => any
thisCompilation: (...args: any[]) => any
emit: (...args: any[]) => any
assetEmitted: (...args: any[]) => any
afterEmit: (...args: any[]) => any
make: (...args: any[]) => any
optimizeModules: (...args: any[]) => any
Expand Down
2 changes: 2 additions & 0 deletions crates/node_binding/src/hook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub enum Hook {
ProcessAssetsStageOptimizeHash,
ProcessAssetsStageReport,
Emit,
AssetEmitted,
AfterEmit,
OptimizeChunkModules,
BeforeCompile,
Expand Down Expand Up @@ -43,6 +44,7 @@ impl From<String> for Hook {
"processAssetsStageOptimizeHash" => Hook::ProcessAssetsStageOptimizeHash,
"processAssetsStageReport" => Hook::ProcessAssetsStageReport,
"emit" => Hook::Emit,
"assetEmitted" => Hook::AssetEmitted,
"afterEmit" => Hook::AfterEmit,
"optimizeChunkModules" => Hook::OptimizeChunkModules,
"beforeCompile" => Hook::BeforeCompile,
Expand Down
17 changes: 17 additions & 0 deletions crates/node_binding/src/js_values/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,20 @@ impl From<rspack_core::AssetInfo> for JsAssetInfo {
}
}
}

#[napi(object)]
pub struct JsAssetEmittedArgs {
pub filename: String,
pub output_path: String,
pub target_path: String,
}

impl From<&rspack_core::AssetEmittedArgs<'_>> for JsAssetEmittedArgs {
fn from(args: &rspack_core::AssetEmittedArgs) -> Self {
Self {
filename: args.filename.to_string(),
output_path: args.output_path.to_string_lossy().to_string(),
target_path: args.target_path.to_string_lossy().to_string(),
}
}
}
1 change: 1 addition & 0 deletions crates/node_binding/src/js_values/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub struct JsHooks {
pub compilation: JsFunction,
pub this_compilation: JsFunction,
pub emit: JsFunction,
pub asset_emitted: JsFunction,
pub after_emit: JsFunction,
pub make: JsFunction,
pub optimize_modules: JsFunction,
Expand Down
23 changes: 21 additions & 2 deletions crates/node_binding/src/plugins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use rspack_napi_shared::threadsafe_function::{ThreadsafeFunction, ThreadsafeFunc
use rspack_napi_shared::NapiResultExt;

use crate::js_values::{
AfterResolveData, BeforeResolveData, JsChunkAssetArgs, JsResolveForSchemeInput,
JsResolveForSchemeResult,
AfterResolveData, BeforeResolveData, JsAssetEmittedArgs, JsChunkAssetArgs,
JsResolveForSchemeInput, JsResolveForSchemeResult,
};
use crate::{DisabledHooks, Hook, JsCompilation, JsHooks};

Expand All @@ -35,6 +35,7 @@ pub struct JsHooksAdapter {
pub process_assets_stage_optimize_hash_tsfn: ThreadsafeFunction<(), ()>,
pub process_assets_stage_report_tsfn: ThreadsafeFunction<(), ()>,
pub emit_tsfn: ThreadsafeFunction<(), ()>,
pub asset_emitted_tsfn: ThreadsafeFunction<JsAssetEmittedArgs, ()>,
pub after_emit_tsfn: ThreadsafeFunction<(), ()>,
pub optimize_modules_tsfn: ThreadsafeFunction<JsCompilation, ()>,
pub optimize_chunk_modules_tsfn: ThreadsafeFunction<JsCompilation, ()>,
Expand Down Expand Up @@ -473,6 +474,20 @@ impl rspack_core::Plugin for JsHooksAdapter {
.map_err(|err| internal_error!("Failed to call emit: {err}"))?
}

async fn asset_emitted(&self, args: &rspack_core::AssetEmittedArgs) -> rspack_error::Result<()> {
if self.is_hook_disabled(&Hook::AssetEmitted) {
return Ok(());
}

let args: JsAssetEmittedArgs = args.into();
self
.asset_emitted_tsfn
.call(args, ThreadsafeFunctionCallMode::NonBlocking)
.into_rspack_result()?
.await
.map_err(|err| internal_error!("Failed to call asset emitted: {err}"))?
}

async fn after_emit(&mut self, _: &mut rspack_core::Compilation) -> rspack_error::Result<()> {
if self.is_hook_disabled(&Hook::AfterEmit) {
return Ok(());
Expand Down Expand Up @@ -502,6 +517,7 @@ impl JsHooksAdapter {
this_compilation,
compilation,
emit,
asset_emitted,
after_emit,
optimize_modules,
optimize_chunk_module,
Expand Down Expand Up @@ -532,6 +548,8 @@ impl JsHooksAdapter {
let process_assets_stage_report_tsfn: ThreadsafeFunction<(), ()> =
js_fn_into_theadsafe_fn!(process_assets_stage_report, env);
let emit_tsfn: ThreadsafeFunction<(), ()> = js_fn_into_theadsafe_fn!(emit, env);
let asset_emitted_tsfn: ThreadsafeFunction<JsAssetEmittedArgs, ()> =
js_fn_into_theadsafe_fn!(asset_emitted, env);
let after_emit_tsfn: ThreadsafeFunction<(), ()> = js_fn_into_theadsafe_fn!(after_emit, env);
let this_compilation_tsfn: ThreadsafeFunction<JsCompilation, ()> =
js_fn_into_theadsafe_fn!(this_compilation, env);
Expand Down Expand Up @@ -575,6 +593,7 @@ impl JsHooksAdapter {
compilation_tsfn,
this_compilation_tsfn,
emit_tsfn,
asset_emitted_tsfn,
after_emit_tsfn,
optimize_modules_tsfn,
optimize_chunk_modules_tsfn,
Expand Down
25 changes: 23 additions & 2 deletions crates/rspack_core/src/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ use rustc_hash::FxHashSet as HashSet;
use tokio::sync::RwLock;
use tracing::instrument;

use crate::{cache::Cache, fast_set, CompilerOptions, Plugin, PluginDriver, SharedPluginDriver};
use crate::{
cache::Cache, fast_set, AssetEmittedArgs, CompilerOptions, Plugin, PluginDriver,
SharedPluginDriver,
};

#[derive(Debug)]
pub struct Compiler<T>
Expand Down Expand Up @@ -242,7 +245,7 @@ where
.emit(&mut self.compilation)
.await?;

let _ = self
let results = self
.compilation
.assets()
.iter()
Expand All @@ -255,6 +258,10 @@ where
Some(self.emit_asset(&self.options.output.path, filename, asset))
})
.collect::<FuturesResults<_>>();
// return first error
for item in results.into_inner() {
item?;
}

self
.plugin_driver
Expand Down Expand Up @@ -296,6 +303,20 @@ where
}

self.compilation.emitted_assets.insert(filename.to_string());

let asset_emitted_args = AssetEmittedArgs {
filename,
output_path,
source: source.clone(),
target_path: file_path.as_path(),
compilation: &self.compilation,
};
self
.plugin_driver
.read()
.await
.asset_emitted(&asset_emitted_args)
.await?;
}
Ok(())
}
Expand Down
10 changes: 7 additions & 3 deletions crates/rspack_core/src/plugin/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ use rspack_sources::BoxSource;
use rustc_hash::FxHashMap as HashMap;

use crate::{
AdditionalChunkRuntimeRequirementsArgs, AssetInfo, BoxLoader, BoxModule, ChunkAssetArgs,
ChunkHashArgs, Compilation, CompilationArgs, CompilerOptions, ContentHashArgs, DoneArgs,
FactorizeArgs, JsChunkHashArgs, Module, ModuleArgs, ModuleFactoryResult, ModuleType,
AdditionalChunkRuntimeRequirementsArgs, AssetEmittedArgs, AssetInfo, BoxLoader, BoxModule,
ChunkAssetArgs, ChunkHashArgs, Compilation, CompilationArgs, CompilerOptions, ContentHashArgs,
DoneArgs, FactorizeArgs, JsChunkHashArgs, Module, ModuleArgs, ModuleFactoryResult, ModuleType,
NormalModule, NormalModuleAfterResolveArgs, NormalModuleBeforeResolveArgs,
NormalModuleFactoryContext, OptimizeChunksArgs, ParserAndGenerator, PluginContext,
ProcessAssetsArgs, RenderArgs, RenderChunkArgs, RenderManifestArgs, RenderModuleContentArgs,
Expand Down Expand Up @@ -366,6 +366,10 @@ pub trait Plugin: Debug + Send + Sync {
Ok(())
}

async fn asset_emitted(&self, _args: &AssetEmittedArgs) -> Result<()> {
Ok(())
}

async fn after_emit(&mut self, _compilation: &mut Compilation) -> Result<()> {
Ok(())
}
Expand Down
11 changes: 10 additions & 1 deletion crates/rspack_core/src/plugin/args.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::fmt::Debug;
use std::path::PathBuf;
use std::path::{Path, PathBuf};

use rspack_error::{internal_error, Result};
use rspack_hash::RspackHash;
Expand All @@ -25,6 +25,15 @@ pub struct ProcessAssetsArgs<'me> {
pub compilation: &'me mut Compilation,
}

#[derive(Debug)]
pub struct AssetEmittedArgs<'me> {
pub filename: &'me str,
pub source: BoxSource,
pub output_path: &'me Path,
pub compilation: &'me Compilation,
pub target_path: &'me Path,
}

#[derive(Debug)]
pub struct ContentHashArgs<'c> {
pub chunk_ukey: ChunkUkey,
Expand Down
16 changes: 12 additions & 4 deletions crates/rspack_core/src/plugin/plugin_driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ use rustc_hash::FxHashMap as HashMap;
use tracing::instrument;

use crate::{
AdditionalChunkRuntimeRequirementsArgs, ApplyContext, BoxLoader, BoxedParserAndGeneratorBuilder,
Chunk, ChunkAssetArgs, ChunkContentHash, ChunkHashArgs, Compilation, CompilationArgs,
CompilerOptions, Content, ContentHashArgs, DoneArgs, FactorizeArgs, JsChunkHashArgs, Module,
ModuleArgs, ModuleType, NormalModule, NormalModuleAfterResolveArgs,
AdditionalChunkRuntimeRequirementsArgs, ApplyContext, AssetEmittedArgs, BoxLoader,
BoxedParserAndGeneratorBuilder, Chunk, ChunkAssetArgs, ChunkContentHash, ChunkHashArgs,
Compilation, CompilationArgs, CompilerOptions, Content, ContentHashArgs, DoneArgs, FactorizeArgs,
JsChunkHashArgs, Module, ModuleArgs, ModuleType, NormalModule, NormalModuleAfterResolveArgs,
NormalModuleBeforeResolveArgs, NormalModuleFactoryContext, OptimizeChunksArgs, Plugin,
PluginAdditionalChunkRuntimeRequirementsOutput, PluginBuildEndHookOutput,
PluginChunkHashHookOutput, PluginCompilationHookOutput, PluginContext, PluginFactorizeHookOutput,
Expand Down Expand Up @@ -566,6 +566,14 @@ impl PluginDriver {
Ok(())
}

#[instrument(name = "plugin:asset_emitted", skip_all)]
pub async fn asset_emitted(&self, args: &AssetEmittedArgs<'_>) -> Result<()> {
for plugin in &self.plugins {
plugin.asset_emitted(args).await?;
}
Ok(())
}

#[instrument(name = "plugin:after_emit", skip_all)]
pub async fn after_emit(&mut self, compilation: &mut Compilation) -> Result<()> {
for plugin in &mut self.plugins {
Expand Down
20 changes: 20 additions & 0 deletions packages/rspack/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ class Compiler {
beforeRun: tapable.AsyncSeriesHook<[Compiler]>;
run: tapable.AsyncSeriesHook<[Compiler]>;
emit: tapable.AsyncSeriesHook<[Compilation]>;
assetEmitted: tapable.AsyncSeriesHook<[string, any]>;
afterEmit: tapable.AsyncSeriesHook<[Compilation]>;
failed: tapable.SyncHook<[Error]>;
shutdown: tapable.AsyncSeriesHook<[]>;
Expand Down Expand Up @@ -203,6 +204,7 @@ class Compiler {
beforeRun: new tapable.AsyncSeriesHook(["compiler"]),
run: new tapable.AsyncSeriesHook(["compiler"]),
emit: new tapable.AsyncSeriesHook(["compilation"]),
assetEmitted: new tapable.AsyncSeriesHook(["file", "info"]),
afterEmit: new tapable.AsyncSeriesHook(["compilation"]),
thisCompilation: new tapable.SyncHook<[Compilation, CompilationParams]>([
"compilation",
Expand Down Expand Up @@ -284,6 +286,7 @@ class Compiler {
afterCompile: this.#afterCompile.bind(this),
make: this.#make.bind(this),
emit: this.#emit.bind(this),
assetEmitted: this.#assetEmitted.bind(this),
afterEmit: this.#afterEmit.bind(this),
processAssetsStageAdditional: this.#processAssets.bind(
this,
Expand Down Expand Up @@ -563,6 +566,7 @@ class Compiler {
beforeCompile: this.hooks.beforeCompile,
afterCompile: this.hooks.afterCompile,
emit: this.hooks.emit,
assetEmitted: this.hooks.assetEmitted,
afterEmit: this.hooks.afterEmit,
processAssetsStageAdditional:
this.compilation.__internal_getProcessAssetsHookByStage(
Expand Down Expand Up @@ -719,6 +723,22 @@ class Compiler {
await this.hooks.emit.promise(this.compilation);
this.#updateDisabledHooks();
}
async #assetEmitted(args: binding.JsAssetEmittedArgs) {
const filename = args.filename;
const info = {
compilation: this.compilation,
outputPath: args.outputPath,
targetPath: args.targetPath,
get source() {
return this.compilation.getAsset(args.filename)?.source;
},
get content() {
return this.source?.buffer();
}
};
await this.hooks.assetEmitted.promise(filename, info);
this.#updateDisabledHooks();
}

async #afterEmit() {
await this.hooks.afterEmit.promise(this.compilation);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
it("should compile successfully with asset-emitted ", () => {
expect(3).toBe(3);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const path = require("path");
const assert = require("assert").strict;
const pluginName = "plugin";

class Plugin {
apply(compiler) {
let hasMainJs = false;
compiler.hooks.assetEmitted.tap(pluginName, (filename, info) => {
if (filename === "main.js") {
assert(info.outputPath === path.resolve(__dirname, "dist"));
assert(info.targetPath === path.resolve(__dirname, "dist/main.js"));
assert(info.content.toString().includes("expect(3).toBe(3)"));
hasMainJs = true;
}
});
compiler.hooks.done.tap(pluginName, () => {
assert(hasMainJs);
});
}
}

/**@type {import('@rspack/cli').Configuration}*/
module.exports = {
context: __dirname,
plugins: [new Plugin()]
};

0 comments on commit d6bebb4

Please sign in to comment.