Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ Rust tooling for building Minecraft resource packs with incremental TypeScript a
- Use `pub(crate)` for internals, `///` for public APIs, and `dep:` for optional dependencies.
- Keep shared dependency versions in `[workspace.dependencies]`.
- Write focused behavior tests. Keep comments sparse and about current behavior.
- TypeScript plugins run on V8 and import the SDK as `#rpp`; config uses `#rpp/config`.
- TypeScript plugins run on V8 and import the SDK as `rpp`; config uses `rpp:config`.
Plugin modules are imported as `plugin:<name>` and `plugin:<name>/<subpath>`.

## Git

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ A project has two files at its root. `rpp.config.ts` describes the pack and the
plugins it runs:

```ts
import { defineConfig } from "#rpp/config";
import jsonMinify from "#plugins/json-minify";
import { defineConfig } from "rpp:config";
import jsonMinify from "plugin:json-minify";

export default defineConfig({
pack: { name: "my-pack", description: "My resource pack", format: 34 },
Expand All @@ -106,7 +106,7 @@ version ranges or local directories:
A plugin is a directory with an `rpp.json` manifest and a TypeScript entry point:

```ts
import { definePlugin } from "#rpp";
import { definePlugin } from "rpp";

export default definePlugin<{ greeting?: string }>({
// Processors run on every matching file.
Expand Down
4 changes: 2 additions & 2 deletions crates/rpp-cli/src/codegen/component_dts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pub fn generate(name: &str, resolve: &Resolve, world: WorldId) -> String {
}
out.push_str(&format!("export interface {exports_name} {exports}\n"));
out.push_str(&format!(
"declare module \"#rpp\" {{\n interface ComponentMap {{\n {}: {exports_name};\n }}\n}}\n",
"declare module \"rpp\" {{\n interface ComponentMap {{\n {}: {exports_name};\n }}\n}}\n",
quote_key(name)
));
out
Expand Down Expand Up @@ -459,7 +459,7 @@ world w { export tools; export run: func(); }"#);
assert!(out.contains("export interface Compiler {\n run: () => void;\n tools: {\n frob: (n: bigint) => bigint;\n };\n}"), "{out}");
assert!(
out.ends_with(
"declare module \"#rpp\" {\n interface ComponentMap {\n compiler: Compiler;\n }\n}\n"
"declare module \"rpp\" {\n interface ComponentMap {\n compiler: Compiler;\n }\n}\n"
),
"{out}"
);
Expand Down
27 changes: 17 additions & 10 deletions crates/rpp-cli/src/codegen/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
//! TypeScript editor and type-checker support: the SDK under `.rpp/sdk` and the
//! tsconfig files that map `#rpp` onto it.
//! tsconfig files that map `rpp` onto it.

mod component_dts;
mod tsconfig;

use std::collections::BTreeMap;
use std::path::Path;

use anyhow::{Context, Result};

use rpp::manifest::PluginManifest;
use rpp::manifest::{plugin_modules, PluginManifest};
use rpp_fetch::registry::PACKAGE_MANIFEST;

use self::tsconfig::{tsconfig, ROOT_TSCONFIG};
Expand All @@ -19,19 +18,27 @@ use crate::project::{is_plugin_manifest, resolve_ts_packages, CONFIG_FILE};
/// Returns whether any file changed.
pub fn write(root: &Path) -> Result<bool> {
let ts_project = root.join(CONFIG_FILE).is_file();
let plugin_configs = if ts_project {
let plugin_modules = if ts_project {
let packages = resolve_ts_packages(root)?;
Some(
packages
.into_iter()
.filter_map(|(name, package)| {
let config = package.manifest.config?;
Some((name, package.dir.join(config)))
.iter()
.flat_map(|(name, package)| {
let manifest = &package.manifest;
plugin_modules(name, manifest.config.as_deref(), &manifest.exports)
.into_iter()
.map(|(specifier, module)| (specifier, package.dir.join(module)))
})
.collect(),
)
} else if is_plugin_manifest(&root.join(PACKAGE_MANIFEST)) {
Some(BTreeMap::new())
let manifest = PluginManifest::load(root)?;
Some(
plugin_modules(&manifest.id, manifest.config.as_deref(), &manifest.exports)
.into_iter()
.map(|(specifier, module)| (specifier, root.join(module)))
.collect(),
)
} else {
None
};
Expand All @@ -43,7 +50,7 @@ pub fn write(root: &Path) -> Result<bool> {
changed |= write_if_changed(&rpp_dir.join("sdk/globals.d.ts"), rpp_js::GLOBALS_DTS)?;
changed |= write_if_changed(
&rpp_dir.join("tsconfig.json"),
&tsconfig(plugin_configs.as_ref()),
&tsconfig(plugin_modules.as_ref()),
)?;

changed |= write_component_dts(root)?;
Expand Down
32 changes: 16 additions & 16 deletions crates/rpp-cli/src/codegen/tsconfig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ const TSCONFIG_HEAD: &str = r##"{
"skipLibCheck": true,
"types": [],
"jsx": "react-jsx",
"jsxImportSource": "#rpp/jsx",
"jsxImportSource": "rpp:jsx",
"paths": {
"#rpp": ["./sdk/index.ts"],
"#rpp/jsx": ["./sdk/jsx.ts"],
"#rpp/jsx/jsx-runtime": ["./sdk/jsx.ts"]"##;
"rpp": ["./sdk/index.ts"],
"rpp:jsx": ["./sdk/jsx.ts"],
"rpp:jsx/jsx-runtime": ["./sdk/jsx.ts"]"##;

const TSCONFIG_TAIL: &str = r#"
}
Expand All @@ -36,14 +36,14 @@ pub(super) const ROOT_TSCONFIG: &str = r#"{
}
"#;

/// The `.rpp/tsconfig.json` contents. `plugin_configs` maps dependency names to their
/// config modules; it is `Some` for `rpp.config.ts` and plugin projects, which also map
/// `#rpp/config`.
pub(super) fn tsconfig(plugin_configs: Option<&BTreeMap<String, PathBuf>>) -> String {
/// The `.rpp/tsconfig.json` contents. `plugin_modules` maps dependency module specifiers
/// (`plugin:<name>[/<subpath>]`) to their files; it is `Some` for `rpp.config.ts` and plugin
/// projects, which also map `rpp:config`.
pub(super) fn tsconfig(plugin_modules: Option<&BTreeMap<String, PathBuf>>) -> String {
let mut text = TSCONFIG_HEAD.to_string();
if let Some(plugin_configs) = plugin_configs {
text.push_str(",\n \"#rpp/config\": [\"./sdk/config.ts\"]");
for (name, path) in plugin_configs {
if let Some(plugin_modules) = plugin_modules {
text.push_str(",\n \"rpp:config\": [\"./sdk/config.ts\"]");
for (specifier, path) in plugin_modules {
let declaration = match path.extension().and_then(|extension| extension.to_str()) {
Some("js") => path.with_extension("d.ts"),
Some("mjs") => path.with_extension("d.mts"),
Expand All @@ -61,7 +61,7 @@ pub(super) fn tsconfig(plugin_configs: Option<&BTreeMap<String, PathBuf>>) -> St
.strip_prefix(r"\\?\")
.unwrap_or(&path)
.replace('\\', "/");
text.push_str(&format!(",\n \"#plugins/{name}\": [{path:?}]"));
text.push_str(&format!(",\n {specifier:?}: [{path:?}]"));
}
}
text.push_str(TSCONFIG_TAIL);
Expand All @@ -78,7 +78,7 @@ mod tests {
let options = &generated["compilerOptions"];
assert_eq!(options["jsxImportSource"], rpp::js::JSX_IMPORT_SOURCE);
assert_eq!(
options["paths"]["#rpp/jsx/jsx-runtime"],
options["paths"]["rpp:jsx/jsx-runtime"],
serde_json::json!(["./sdk/jsx.ts"])
);
}
Expand All @@ -88,7 +88,7 @@ mod tests {
let root = tempfile::tempdir().unwrap();
let config = root.path().join("config.js");
std::fs::write(&config, "export default () => {};\n").unwrap();
let configs = BTreeMap::from([("packed".to_string(), config.clone())]);
let configs = BTreeMap::from([("plugin:packed".to_string(), config.clone())]);
assert!(tsconfig(Some(&configs)).contains("config.js"));
std::fs::write(
config.with_extension("d.ts"),
Expand All @@ -103,10 +103,10 @@ mod tests {
#[test]
fn verbatim_windows_paths_are_mapped_as_drive_paths() {
let configs = BTreeMap::from([(
"local".to_string(),
"plugin:local".to_string(),
PathBuf::from(r"\\?\C:\plugins\local\config.ts"),
)]);
assert!(tsconfig(Some(&configs))
.contains(r##""#plugins/local": ["C:/plugins/local/config.ts"]"##));
.contains(r##""plugin:local": ["C:/plugins/local/config.ts"]"##));
}
}
2 changes: 1 addition & 1 deletion crates/rpp-cli/src/commands/dev/rebuild.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ mod tests {
use super::super::watch::spawn_watcher;
use super::*;

const CONFIG: &str = r##"import { defineConfig } from "#rpp/config";
const CONFIG: &str = r##"import { defineConfig } from "rpp:config";
export default defineConfig({ pack: { name: "test", format: 34 } });
"##;

Expand Down
4 changes: 2 additions & 2 deletions crates/rpp-cli/src/commands/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ fn rpp_config(name: &str, description: &str, pack_format: u32) -> String {
let name = ts_string(name);
let description = ts_string(description);
format!(
r##"import {{ defineConfig, plugin }} from "#rpp/config";
r##"import {{ defineConfig, plugin }} from "rpp:config";

export default defineConfig({{
pack: {{
Expand Down Expand Up @@ -180,7 +180,7 @@ const HELLO_MANIFEST: &str = r#"{
}
"#;

const HELLO_PLUGIN: &str = r##"import { definePlugin } from "#rpp";
const HELLO_PLUGIN: &str = r##"import { definePlugin } from "rpp";

// Processor: minify every JSON / mcmeta file in the pack.
// Generator: emit a tiny build marker listing the pack name and greeting.
Expand Down
31 changes: 25 additions & 6 deletions crates/rpp-cli/src/commands/plugin/pack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ pub fn pack(dir: &Path, out: &Path) -> Result<Packed> {
root: dir.to_path_buf(),
plugin: manifest.entry.clone(),
config: manifest.config.clone(),
self_specifier: Some(format!("#plugins/{}", manifest.id)),
exports: manifest.exports.clone(),
self_specifier: Some(format!("plugin:{}", manifest.id)),
jsx_import_source: Some(JSX_IMPORT_SOURCE.to_string()),
})?;
let files = collect_files(dir, &text, &manifest, bundled)?;
Expand Down Expand Up @@ -107,6 +108,14 @@ fn packed_manifest(text: &str, bundled: &PackOutput) -> Result<Vec<u8>> {
if let Some(config) = &bundled.config {
object.insert("config".into(), config.clone().into());
}
if !bundled.exports.is_empty() {
let exports = bundled
.exports
.iter()
.map(|(subpath, path)| (format!("./{subpath}"), Value::from(path.clone())))
.collect();
object.insert("exports".into(), Value::Object(exports));
}
Ok(format!("{}\n", serde_json::to_string_pretty(&raw)?).into_bytes())
}

Expand All @@ -124,21 +133,31 @@ fn write_outputs(out: &Path, file: &str, archive: &[u8]) -> Result<(PathBuf, Str
}

/// Unpacks the archive as installs do and bundles each entry with only the SDK modules
/// its loader provides: `#rpp` for the plugin, `#rpp/config` for the config, and `#rpp/jsx`.
/// its loader provides: `rpp:config` for the config, and `rpp` too for the plugin and
/// exports (pack sources import them), plus `rpp:jsx`.
fn self_check(archive: &[u8]) -> Result<()> {
let dir = tempfile::tempdir()?;
rpp_archive::unpack(archive, dir.path()).context("unpacking the archive")?;
let manifest = PluginManifest::load(dir.path()).context("checking the packed manifest")?;
let mut entries = vec![(manifest.entry, "#rpp", SDK_INDEX)];
let sdk = ("rpp", SDK_INDEX);
let config_sdk = ("rpp:config", SDK_CONFIG);
let mut entries = vec![(manifest.entry, vec![sdk, config_sdk])];
if let Some(config) = manifest.config {
entries.push((config, "#rpp/config", SDK_CONFIG));
entries.push((config, vec![config_sdk]));
}
for (entry, specifier, source) in entries {
entries.extend(
manifest
.exports
.into_values()
.map(|module| (module, vec![sdk, config_sdk])),
);
for (entry, sdk_modules) in entries {
let request = BundleRequest {
root: dir.path().to_path_buf(),
entry: entry.clone(),
virtual_modules: [(specifier.to_string(), source.to_string())]
virtual_modules: sdk_modules
.into_iter()
.map(|(specifier, source)| (specifier.to_string(), source.to_string()))
.chain(jsx_modules())
.collect(),
jsx_import_source: Some(JSX_IMPORT_SOURCE.to_string()),
Expand Down
1 change: 1 addition & 0 deletions crates/rpp-cli/src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ fn load_ts(root: &Path) -> Result<(Config, TsProject)> {
ConfigPackage {
dir: package.dir.clone(),
config: package.manifest.config.clone(),
exports: package.manifest.exports.clone(),
},
)
})
Expand Down
4 changes: 2 additions & 2 deletions crates/rpp-cli/tests/build_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ use common::{build, stderr, write};
/// An `rpp.config.ts` whose `defineConfig` object body is `body`.
fn config_ts(body: &str) -> String {
format!(
"import {{ defineConfig, plugin }} from \"#rpp/config\";\n\nexport default defineConfig({{\n{body}\n}});\n"
"import {{ defineConfig, plugin }} from \"rpp:config\";\n\nexport default defineConfig({{\n{body}\n}});\n"
)
}

/// A plugin that minifies every `data.json`.
const MINIFY_PLUGIN: &str = r##"import { definePlugin } from "#rpp";
const MINIFY_PLUGIN: &str = r##"import { definePlugin } from "rpp";

export default definePlugin({
processors: {
Expand Down
8 changes: 4 additions & 4 deletions crates/rpp-cli/tests/codegen_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ fn codegen_writes_sdk_and_tsconfig() {
assert!(globals.contains("declare var crypto"));
assert!(!globals.contains("structuredClone"));
let tsconfig = std::fs::read_to_string(root.join(".rpp/tsconfig.json")).unwrap();
assert!(tsconfig.contains("\"#rpp\": [\"./sdk/index.ts\"]"));
assert!(tsconfig.contains("\"rpp\": [\"./sdk/index.ts\"]"));
let root_config = std::fs::read_to_string(root.join("tsconfig.json")).unwrap();
assert!(root_config.contains("./.rpp/tsconfig.json"));
assert!(root_config.contains("**/*.mts"));
Expand Down Expand Up @@ -71,7 +71,7 @@ fn codegen_works_in_json_plugin_dir() {
);
let tsconfig = std::fs::read_to_string(plugin.join(".rpp/tsconfig.json")).unwrap();
assert!(
tsconfig.contains("\"#rpp/config\": [\"./sdk/config.ts\"]"),
tsconfig.contains("\"rpp:config\": [\"./sdk/config.ts\"]"),
"{tsconfig}"
);
}
Expand Down Expand Up @@ -264,7 +264,7 @@ fn build_runs_typescript_plugin() {
let root = dir.path();
std::fs::write(
root.join("rpp.config.ts"),
"import { defineConfig, plugin } from \"#rpp/config\";\n\
"import { defineConfig, plugin } from \"rpp:config\";\n\
export default defineConfig({ pack: { name: \"p\", format: 34 }, plugins: [plugin(\"upper\")] });\n",
)
.unwrap();
Expand All @@ -284,7 +284,7 @@ fn build_runs_typescript_plugin() {
.unwrap();
std::fs::write(
plugin.join("src/plugin.ts"),
r##"import { definePlugin } from "#rpp";
r##"import { definePlugin } from "rpp";

export default definePlugin({
processors: {
Expand Down
4 changes: 2 additions & 2 deletions crates/rpp-cli/tests/common/packed_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub fn plugin(root: &Path) {
write(
root,
"src/config.ts",
r##"import { definePluginConfig, type Access, type PluginEntry } from "#rpp/config";
r##"import { definePluginConfig, type Access, type PluginEntry } from "rpp:config";

type Options = { text: string };
const config: (options: Options, access?: Access) => PluginEntry = definePluginConfig<Options>(
Expand All @@ -53,7 +53,7 @@ export default config;
write(
root,
"src/plugin.ts",
r##"import { definePlugin } from "#rpp";
r##"import { definePlugin } from "rpp";
import { mark } from "dep";
import { check } from "./helper";

Expand Down
6 changes: 3 additions & 3 deletions crates/rpp-cli/tests/examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,8 @@ fn hash_rename_uses_processed_bytes() {
write(
root,
"rpp.config.ts",
r##"import { defineConfig, plugin } from "#rpp/config";
import hashRename from "#plugins/hash-rename";
r##"import { defineConfig, plugin } from "rpp:config";
import hashRename from "plugin:hash-rename";

export default defineConfig({
pack: { name: "processed-hash", format: 34 },
Expand All @@ -248,7 +248,7 @@ export default defineConfig({
write(
root,
"plugins/modify/plugin.ts",
r##"import { definePlugin } from "#rpp";
r##"import { definePlugin } from "rpp";

export default definePlugin({
processors: {
Expand Down
2 changes: 1 addition & 1 deletion crates/rpp-cli/tests/examples_wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ fn grayscale_project(tmp: &Path) -> PathBuf {
write(
&root,
"rpp.config.ts",
br##"import { defineConfig, plugin } from "#rpp/config";
br##"import { defineConfig, plugin } from "rpp:config";

export default defineConfig({
pack: { name: "gray", format: 34 },
Expand Down
Loading
Loading